Getting The Image SRC Of ColdFusion's CFImage WriteToBrowser Temporary Image With CFXML
ColdFusion 8 comes with a file servlet that can be used to serve up temporary image files. This is great because it allows us to do on-the-fly image creation without having to worry about the physical image file I/O (input/output). Furthermore, the images stored in the file servlet get deleted automatically after a brief amount of time so that we don't have to worry about file clean-up either. Unfortunately, there's no real API for accessing this file servlet; it's usage is a byproduct of some of ColdFusion's other features. Specifically, ColdFusion's CFImage "WriteToBrowser" action will write an image to the file servlet and then output an XHTML IMG tag with the temporary image path as the IMG SRC attribute.
Sometimes, it would be nice to be able to create these temporary image files without having to write an IMG tag to the output. Fortunately, there is a really easy way to leverage the existing CFImage behavior to accomplish such a goal. As I demonstrated a few years ago, the CFIMage "WriteToBrowser" action doesn't necessarily write an IMG tag to the response buffer; rather, it writes the IMG tag to the current output buffer. In most cases, the response buffer is the current output buffer; but, when we use ColdFusion tags like CFSaveContent and CFXML, the output buffer within those tags is not the response buffer but rather the content buffer of the given tag.
A while back, I used a CFSaveContent approach to extract the SRC attribute of the temporary IMG tag. But, there's an even easier approach. When CFImage writes the IMG tag to the current output buffer, it uses strict XHTML standards. That means it also uses XML standards. And, if it uses XML standards, we can treat it as XML and parse it into an XML document object. This turns out to be super easy way to access the temporary image source path without having to output the IMG tag to the response:
<!--- Load the remote image. --->
<cfimage
name="kittenYawning"
action="read"
source="http://some-image-domain.com/38sdf52_o.jpg"
/>
<!---
Write the image to the output. Except, rather than writing it
to the screen, write it to an XML data buffer.
--->
<cfxml variable="imageXml">
<!--- Let IMG be the only tag in this XML document. --->
<cfimage
action="writetobrowser"
source="#kittenYawning#"
/>
</cfxml>
<!---
At this point, our XML object should have one tag - IMG - from
which we can extract the SRC attribute as the temporary image
path on the CFImage file servlet.
--->
<cfset imageSrc = imageXml.xmlRoot.xmlAttributes.src />
<!--- Output the temporary image src: --->
<cfoutput>
SRC: #imageSrc#
</cfoutput>
As you can see here, after we load the remote image, we use CFImage's "WriteToBrowser" action to output the XHTML IMG tag to a CFXML buffer. This will parse the IMG tag into a single-node XML document from which we can easily extract the XML attribute, "Src." In doing so, we can find the URL of the temporary image without having to send an IMG tag to the client or deal with any complex Regular Expression pattern matching. And, in fact, when we run the above code, we get the following output:
SRC: /CFFileServlet/_cf_image/_cfimg3659208981406976772.PNG
This SRC attribute can then be served up to the client as some sort of AJAX or Web Service response. In either case, I have found this to be the easiest way to create temporary images without having direct access to the response buffer.
Want to use code from this post? Check out the license.
Reader Comments
It's important to remember that there's a pretty short timeout for these temporary files, so the URLs become invalid pretty quickly (I can't recall what the initial timeout is, but it can be changed in one of the XML files.) Just keep this in mind depending on how you're planning on implementing this.
@Dan,
Good point. I think by default it is somewhat high (the timeout); from what I can remember, I've had situations where the URL is still valid like 20 minutes later.
But that said, yes, you can definitely not depend on this for any kind of long-term persistence. Typically what I use this for is AJAX / web services responses where the URL is needed immediately, but an IMG tag is not really fitting to the situation.
I'm writing to recommend exploring the "Data URL" syntax:
http://en.wikipedia.org/wiki/Data_URI_scheme
The advantage of using a true file for an image is caching. On page refreshes or requests of new pages with the same URL, the browser detects that the file is in cache and sends the request with the HTTP header If-Modified-Since:
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25
If the file hasn't changed since the date/time given in the header, the server can respond with a 304 Not Modified response, and the web is all the better off for the minimization of unnecessary traffic.
But if the file is going to reside on the hard drive on the server only temporarily, you lose that benefit. So why have it reside there at all? You're incurring an I/O to write it, the web server incurs an I/O to read it, and the network traffic is the same or worse, compared to using a Data URL.
I've only used the CF image tags/functions to create CAPTCHA files, which are an ideal example of intentionally one-time-only files. But I couldn't explore the Data URL option further at that time, because I had to abandon CAPTCHA. (I work at a US Federal Government site and all of our pages have to be Section 508 compliant. It denies access to the blind to use CAPTCHA to control access.)
Given the ease with which we can base64-encode data in ColdFusion, Data URLs seem like a perfect fit for generated-only-once-on-the-fly images.
I also recommend copying and pasting the w3.org URL I just gave, because the ".25" got chopped off of the hotlink.
You meant Ben's earlier post with the girl with sand on her face, not my wikipedia or w3 URLs, I hope.
@Anon,
Shhh, that's an "Easter egg".
@Steve,
Data URIs are definitely interesting. As you are probably aware, ColdFusion can easily read in data URIs with the imageReadBase64() method. I've briefly played around with that; but, I have not played around very much with actually creating base64 encoded images as part of a data URI.
It is definitely very cool. Unfortunately, I don't think image data URIs are not supported in Internet Explorer until version 8. That's puts a bit of hamper on its use :(
... or even a damper. But it wouldn't be the first time I threw a feature into the laundry bin because of MSIE. :)
@Steve,
Something like this, however, would be awesome for a closed application, like an internal app on the company intranet.
@Ben,
When you said you didn't think that data URIs were supported by MSIE until version 8, it was news to me. I assumed that you had run one of your many experiments.
But just now, on Wikipedia, I noticed that MSIE does in fact support data URIs for images. Have a look:
http://en.wikipedia.org/wiki/Comparison_of_web_browsers#cite_note-IEdataprotocol-132
So I'm back to recommending that approach as a way to avoid 2 I/Os when you're serving up generated-only-once-on-the-fly images.
@Steve,
I am not sure if I have run it. I have definitely looked it up, though not on any official docs - mostly on some other people's web sites. I'll do some testing with my IE Collection app.
you rock ;)
perfect for fancybox ;)