Embedding Base64 Image Data into a Webpage

This is part two of a tutorial for taking a screenshot of a Flash movie and putting it into a webpage using Javascript. As you might expect, Internet Explorer and Firefox have different ways of going about embedding the image data in HTML. Details after the click-through.

This is the second part of a tutorial for dynamically embedding a screenshot of a Flash movie into a webpage. In the previous section, we got image data out of Flash and into a Javascript variable. Now, we'll find out how to display that data using an <img> tag. If you want, you can skip ahead to copying the image to the clipboard.

And, no, this doesn't involve creating a table with 1 x 1 px cells in hundreds of rows and columns. (-;

Here's that demo from last time so you can see where we're going with this.

Alright, so where we left off, we got the base64-encoded PNG data into a Javascript variable like so:

var screenshotBase64 = flashElem.getScreenshot();

Embedding the base64-encoded image in HTML

Now we've left the relatively sheltered environment provided by the Flash Player and we have to deal with some browser compatibility problems. Firefox provides an easy way to embed images dynamically in a webpage. You can use the data: URI scheme to put base64 (or other data) right into the src attribute of an image tag.

<img src="data:image/png;base64,iVBORw0KGgoAAAANS..." />

Base64 Images in Internet Explorer

IE is a little different. If you want to embed images right in the web page for Internet Explorer, you would use MHTML, which attaches the additional images as distinct and separate parts in the request. You can see an example of embedding images into a webpage using MHTML at Hedger Wang's website. It's easiest to figure out what's going on by looking at the PHP source code.

Since we're loading images dynamically this doesn't work for us so instead, what we'll do is write a short PHP script which accepts the encoded data and spits out the image. This gives us a URL where the image can be accessed directly, so it's like referring to any other image on the server. This is less than ideal, since it does involve a trip back to the server, but it works.

Here's the script which I got from Dean Edwards' post on displaying Base64-encoded images in IE.

 * an example URL for viewing a base64-encoded image:
 * http://example.com/decode.php?image/png;base64,iVBORw0KGgoAAAANS...
$data = split(";", $_SERVER["QUERY_STRING"]);
$type = $data[0];
$data = split(",", $data[1]);
header("Content-type: ".$type);
echo base64_decode($data[1]);

So now, using this script, we can display the encoded image in Internet Explorer as well, using the following markup.

<img src="http://example.com/decode.php?image/png;base64,iVBORw..." />

The actual markup which embeds the image

To distinguish between Internet Explorer and, well, everyone else, I used conditional comments in the <head> like so:

<script type="text/javascript">
	function getImageSrc(base64Src)
	{ return base64Src; }
<!--[if gte IE 5]>

	<script type="text/javascript">
		function getImageSrc(base64Src)
		{ return "/decode_base64.php?" + base64Src.slice(5); }

Now, I can easily set the src attribute of the <img> element.

// imageElem is the <img> tag that will display the screenshot
imageElem.src = getImageSrc("data:image/png;base64," + screenshotBase64);

And that's all it takes!

Note that the encoding used here is image/png but you can use other formats as well.

Embedding large images directly in HTML

There is a pretty significant problem with the way we worked around the Internet Explorer issue. If you're just embedding little icons then you don't have to worry, but when dealing with larger images, the base64 string can get too long. URLs have a limited length and it doesn't take a very large image to hit that boundary. In my brief tests a 100x100 pixel PNG was okay, but a 200x200 one was not. Note that this problem will only occur in IE since Firefox doesn't go back to the server at all.

To work around this problem I did something a little different. Instead of sending the base64 data to Javascript, I sent it directly to the server-side script in a POST request. The script saves the data to a temporary file and returns an ID for that file to Flash. Flash then calls it's ExternalInterface method, passing the file ID on to Javascript. Now, instead of setting the Image.src attribute to the decode_base64.php script above, you use a different script which takes the file ID as a parameter, sends the appropriate Content-Type header, retrieves the file and prints it to output, and finally, deletes the temporary file.

In the third and last part of this little tutorial, we're going to copying the screenshot to the clipboard.