Creating Base64-Encoded Data URLs For Images In ColdFusion
Typically, when we want to include an image in a web page, we use an IMG tag with a SRC value that points to a file located on the server (ex. my_image.jpg). Modern browsers, however, allow us to embed images directly in the page markup as Base64-encoded "data urls." For caching and performance reasons, you generally wouldn't want to use data urls to define your images; however, in some cases, it's tremendously useful to serialize and reference an image without needing an additional HTTP request.
At this last CFUNITED, I was talking to Todd Sharp about these data urls; specifically, I was complaining that in ColdFusion, converting an Image object to a Base64-encoded string required an intermediary file-write using the imageWriteBase64() function. I suggested that it would be cool if ColdFusion would provide a way to execute this conversion without the intermediary file-write.
Todd then blew my mind by dropping this little bomb shell: You can just use the toBase64() function to convert ColdFusion image objects to Base64-encoded strings.
That thought had never even crossed my mind - ColdFusion image data always felt too encapsulated within a ColdFusion class to be treated as actual binary content. As it turns out, though, Todd was absolutely correct. In fact, you can use the toBase64() function to convert any image binary to Base64():
<!--- Create a ColdFusion image object by reading the photo off of the local disk. ---> <cfimage name="photo" action="read" source="./flex_bikini.jpg" /> <!--- Now that we have a ColdFusion image representation of the photo, write it out to disk as a base64 encoded string. NOTE: The "true" argument is to write the image as HTML format for Base64 data urls (included the "data:image/*;base64," prexi for the IMG SRC). ---> <cfset imageWriteBase64( photo, expandPath( "./base64Image.txt" ), "jpg", true ) /> <!--- Embed the image as a Base64-encoded data URL image. ---> <cfoutput> <h2> ColdFusion Image To Base64 TXT File To Data URL </h2> <img src="#fileRead( expandPath( "./base64Image.txt" ) )#" height="400" /> </cfoutput> <!--- ----------------------------------------------------- ---> <br /><br /> <!--- ----------------------------------------------------- ---> <!--- Create a ColdFusion image object reading the photo off of the local disk. ---> <cfimage name="photo" action="read" source="./flex_bikini.jpg" /> <!--- Embed the image as a Base64-encoded data URL image. ---> <cfoutput> <h2> ColdFusion Image To Base64 </h2> <img src="data:image/*;base64,#toBase64( photo )#" height="400" /> </cfoutput> <!--- ----------------------------------------------------- ---> <br /><br /> <!--- ----------------------------------------------------- ---> <!--- Read the photo off of the local disk; but this time, read it in as binary object without creating a ColdFusion image object. ---> <cfset photoBinary = fileReadBinary( expandPath( "./flex_bikini.jpg" ) ) /> <!--- Embed the image as a Base64-encoded data URL image. ---> <cfoutput> <h2> File Binary To Base64 </h2> <img src="data:image/*;base64,#toBase64( photoBinary )#" height="400" /> </cfoutput>
As you can see here, I am using three different approaches to creating Base64 data urls. The first approach uses the imageWriteBase64() function that I had thought was required. The second approach passes a ColdFusion image object to the toBase64() function as Todd suggested. The third approach reads an image into memory as raw binary and then uses the toBase64() function. When I run this code, I get the following output:
As you can see, all three approaches worked. Not only can you use the toBase64() function to convert ColdFusion image objects to Base64, you can also use it to convert raw image binaries to Base64-encoded data URLs. This third approach not only bypasses the file-write, it also gets around having to use any ColdFusion image manipulation (assuming none was needed). Thanks Todd!
Want to use code from this post? Check out the license.
Not all browsers support the data URI scheme. IE7 and earlier doesn't support data uri's at all. IE8 has a 32k limit.
Word up! I know it was a bit snarky, but I just assumed that by saying "modern browsers", people would read that as non-IE ;)
Do you know of any plans for IE9?
What's interesting in IE8 about the 32K limit is that it doesn't break with images larger than 32K of base64 data, it simply stops loading the image. When I tested this, my first image came through about 1/3 of the way.
The other two approaches break because I am using "image/*" rather than "image/jpg".
I wonder if that would work with images in an e-mail to get images to load without the user having to "enable" them (no privacy concerns since they'd already be embedded). Hmm...
Good though. It used to work with content-embeded images; but even that seems to require "enable" these days (which makes no sense since there is no linking).
I don't know of any planned changes to IE9, but I doubt it's a focus for them.
I've been using extjs, and this exact issue came up with regards to exporting a grid to excel. We're doing it server side, for several reasons (this is one of them).
I ran across a nifty little flash app, downloadify (http://downloadify.info), that you can use to get around the 32k limit, and add support for older browsers, assuming they have flash available. For file downloads, not for embedded images (have to click to activate the flash).
It makes a certain sense to have to enable them. Otherwise, the porn spammers would do this. No privacy issues, but still NSFW images showing up in your mail.
@Ben: In response to "Do you know of any plans for IE9?", IE9 is going HTML 5.
From Smashing Magazine > Seven Must-See Videos and Presentations for Web App Developers > HTML 5 and Internet Explorer 9 > Watch on YouTube:
(58:55 long, Giorgio Sardo from Microsoft)
The other must-see videos are interesting too:
Downloadify looks pretty cool; I had no idea that that kind of functionality (client-side file generation) was even something that existed.
Re: porn spammers - that had never occurred to me! I had always thought the disabled images thing was about external URLs loading images that could "track" valid email address.
Thanks for the links. I'm happy to hear that IE9 is gonna be HTML5 friendly. When I get some time, I'll check out those videos.
Where _do_ you find your image samples?
@Ben: The Giorgio Sardo video begins with a mock funeral for IE6. "In Memoriam", photo with a casket, flowers, etc. Very satisfying seeing that from Microsoft.
They're adding HTML 5 support to IE9 in a big way. They seem to have finally gotten standards compliance religion.
Re: HTML 5
Open in Chrome:
Played with this yesterday. Speaking of P40n spammers, this looks like a security hole waiting to happen, but for now it's frickin' sweet. I'd suggest using a popular, main-drag address, but in a residential area.
Sorry if it's a bit off-topic, Ben!
It takes a lot of work to find these images :D
Ha ha, awesome; can't wait to check it out. It's exciting that IE9 is finally "cool". We'll just see what kind of adoption rate it gets.
I was watching that the other day. It was kind of cool that it integrated local Google map images with the music video. I don't know much about the p40n spam thing you're talking about though.
Seems like porn spammers could exploit the way Chrome is able to open and close windows the way the video causes Chrome to do. Remember X-10 video cameras? Now Netflix seems to be just as bad. Intrusive windows could start popping up/under your current window and filling up your task bar or tab bar with SPAM possibly to the point where you'd have to kill Chrome to get back control. Just a concern.
At the very least, I think Chrome won't be able to close windows unless it opens them up, right? I am not sure.
Use toBase64(imageGetBlob(myImg)) instead of just toBase64.
I know this thread is old, but I wanted to share with you a work around I found to an issue I was having with the built-in toBase64 method when using PNG files with transparency. The 24-bit PNG files were not rendering correctly on the browser. When I read them with cffile and used BinaryEncode(), all worked correctly.
What I did was read an image stored on the file system into the heap using the cfimage tag. Then, I called the ImageGetBlob() method on the image variable. After that, I called BinaryEncode() and transported the base64 string over JSON back to the server, loaded via jQuery, and woop there it is! This works great for me as all of the images I need fetched over AJAX via JSON are stored in the DB as BLOBS. So, when I initially store the images into the database, this will be the last time I need to manipulate the image in it's lifecycle minus converting them to Base64 for transport. Super excited about this because now I don't need to touch the file system and can save both IO to the disk and cycles for the processing. ColdFusion saves the day again! Some out there have asked the question: "why don't you store the image as a string in a CLOB column?" We all know that stringified images use about 30-35% more disk space, so no thanks.
Thought i would share as this is going to save my butt.
transparent PNG image transfer to base64 shows black background color.
img = imageNew(newFilePath);
WriteOutput('<img src="data:image/*;base64,' & baseimg & '" />');
How to solve this ?