Data URIs and Inline Images

From Jimbojw.com

Jump to: navigation, search
Tip: This is part of the Weekly Web Hack article series. If you like it, please subscribe to my blog. Thanks!

Update: It turns out this does not work in Internet Explorer. I am currently investigating an IE specific alternative, which I plan to reveal in an upcoming Weekly Web Hack article.


In this edition of the Weekly Web Hack, I'll explain how data URIs work and give some practical examples when you might want to use them in your own project.

By the end of this article, you should be able to:

  1. Determine whether a particular use-case is suitable for inline data,
  2. Understand the data: URI scheme and available encodings,
  3. Use data URIs to embed image (and other) data directly into an HTML or CSS file.

Why use data URIs?

Here are some good litmus tests to help you decide if your use-case warrants using data URIs for inline images:

  • Are your images small? (think icon-sized -or- a few Kb at most)
  • Are your images fairly static? That is, are they unlikely to frequently change?
  • Are you using images as backgrounds for CSS classes?
  • Are your images presentationally relevant? (ex: rounded corners)
  • Does your site have high-throughput (bandwidth), but also high latency?

If you find yourself answering "yes" to the questions above, you may want to consider inlining your data. Inline images do not suffer the overhead of an HTTP round trip for the browser to retrieve them. This is especially useful for HTTPS connections, which require additional connection overhead - exaggerating any latency problems.

Also, since they are embedded directly into the page (either HTML or CSS), the image data is present at DOM render time. This means you won't see the all-too-common image flicker that occurs between DOM load and image load. However, the trade-off is that this will make the initial download time for the HTML or CSS that much longer. Using CSS as the storage location for the inline images rather than HTML will help mitigate this for subsequent page-loads since browsers tend to cache CSS files rather aggressively.

For further reading, see the wikipedia entry on the data: URI scheme. It has an excellent list of the technical advantages and disadvantages of using data URIs.

Data URI quick overview

Data URIs are dead simple. An easy way to think of them is by analogy - while a http URL points to content, a data URI actually contains the content.

A data URI follows this format:

data:[<MIME-type>][;base64],<data>

Where <MIME-type> represents the Internet media type of the data. Examples include text/html, image/png and application/pdf. Though data URIs are not generally very popular, when they are employed they are most commonly used for embedding images.

The data can be encoded using either standard URL percent encoding, or using base64 encoding. If the optional ";base64" does not appear in the data URI, percent encoding is assumed.

A simple example

The following URL points to this image of a blueish "page copy" icon (Image:Page copy.png):

Note: Image courtesy of Mark James' fabulous Silk Icons
http://jimbojw.com/userscripts/page_copy.png

Here's the equivalent data URI using base64 encoding:

data:image/png;base64,
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAIp
SURBVDjLddM9aFRBFIbh98zM3WyybnYVf4KSQjBJJVZBixhRixSaShtBMKUoWomgnaCxsJdgIQSstE4nEhNREgyoZYhpkogkuMa4/3fuHIu7gpLd00wz52PO
MzMydu/Dy958dMwYioomIIgqDa+VnWrzebNUejY/NV6nQ8nlR4ufXt0fzm2WgxUgqBInAWdhemGbpcWNN9/XN27PPb1QbRdgjEhPqap2ZUv5+iOwvJnweT1m
T5djZKjI6Ej/udz+wt1OJzAKYgWyDjJWyFghmzFsbtcY2gsTJwv09/Vc7RTgAEQgsqAKaoWsM8wu/z7a8B7vA8cHD3Fr+ktFgspO3a+vrdVfNEulJ/NT4zWn
gCBYY1oqSghKI465fvYwW+VAatPX07IZmF7YfrC0uDE8emPmilOFkHYiBKxAxhmSRPlZVVa2FGOU2Ad2ap4zg92MDBXJZczFmdflx05VEcAZMGIIClZASdes
S2cU/dcm4sTBArNzXTcNakiCb3/HLRsn4Fo2qyXh3WqDXzUlcgYnam3Dl4Hif82dbOiyiBGstSjg4majEpl8rpCNUQUjgkia0M5GVAlBEBFUwflEv12b/Hig
6SmA1iDtzhcsE6eP7LIxAchAtwNVxc1MnhprN/+lh0txErxrPZVdFdRDEEzHT6LWpTbtq+HLSDDiOm2o1uqlyOT37bIhHdKaXoL6pqhq24Dzd96/tUYGwPSB
Vv7atFglaFIu5KLuPxeX/xsp7aR6AAAAAElFTkSuQmCC

And here it is again, using regular URL percent encoding:

data:image/png,
%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%10%00%00%00%10%08%06%00%00%00%1F%F3%FFa%00%00%00%04gAMA%00%00%AF%C87%05%8A%E
9%00%00%00%19tEXtSoftware%00Adobe%20ImageReadyq%C9e%3C%00%00%02%29IDAT8%CBu%D3%3DhTA%14%86%E1%F7%CC%CC%DDl%B2nv%15%7F%82
%92B0I%25VA%8B%18Q%8B%14%9AJ%1BA0%A5%28Z%89%A0%9D%A0%B1%B0%97%60%21%04%AC%B4N%27%12%13Q%12%0C%A8e%88i%92%88%24%B8%C6%B8%
FFw%EE%1C%8B%BB%82%92%DD%D3L3%E7c%CE332v%EF%C3%CB%DE%7Ct%CC%18%8A%8A%26%20%88%2A%0D%AF%95%9Dj%F3y%B3Tz6%3F5%5E%A7C%C9%E5
G%8B%9F%5E%DD%1F%CEm%96%83%15%20%A8%12%27%01gaza%9B%A5%C5%8D7%DF%D77n%CF%3D%BDPm%17%60%8CHO%A9%AAveK%F9%FA%23%B0%BC%99%F
0y%3DfO%97cd%A8%C8%E8H%FF%B9%DC%FE%C2%DDN%270%0Ab%05%B2%0E2V%C8X%21%9B1ln%D7%18%DA%0B%13%27%0B%F4%F7%F5%5C%ED%14%E0%00D%
20%B2%A0%0Aj%85%AC3%CC.%FF%3E%DA%F0%1E%EF%03%C7%07%0Fqk%FAKE%82%CAN%DD%AF%AF%AD%D5_4K%A5%27%F3S%E35%A7%80%20XcZ%2AJ%08J%
23%8E%B9%7E%F60%5B%E5%40j%D3%D7%D3%B2%19%98%5E%D8%7E%B0%B4%B81%3Czc%E6%8AS%85%90v%22%04%AC%40%C6%19%92D%F9YUV%B6%14c%94%
D8%07vj%9E3%83%DD%8C%0C%15%C9e%CC%C5%99%D7%E5%C7NU%11%C0%190b%08%0AV%40I%D7%ACKg%14%FD%D7%26%E2%C4%C1%02%B3s%5D7%0DjH%82
o%7F%C7-%1B%27%E0Z6%AB%25%E1%DDj%83_5%25r%06%27jm%C3%97%81%E2%7F%CD%9Dl%E8%B2%88%11%AC%B5%28%E0%E2f%A3%12%99%7C%AE%90%8D
Q%05%23%82H%9A%D0%CEFT%09A%10%11T%C1%F9D%BF%5D%9B%FCx%A0%E9%29%80%D6%20%ED%CE%17%2C%13%A7%8F%EC%B21%01%C8%40%B7%03U%C5%C
DL%9E%1Ak7%FF%A5%87Kq%12%BCk%3D%95%5D%15%D4C%10L%C7O%A2%D6%A56%ED%AB%E1%CBH0%E2%3Am%A8%D6%EA%A5%C8%E4%F7%ED%B2%21%1D%D2%
9A%5E%82%FA%A6%A8j%DB%80%F3w%DE%BF%B5F%06%C0%F4%81V%FE%DA%B4X%25hR.%E4%A2%EE%3F%17%97%FF%1B%29%ED%A4z%00%00%00%00IEND%AE
B%60%82

Notice that the percent encoded version is quite a bit larger. This will be heavily dependent on the type of data being encoded. Both base64 encoding and percent encoding will be larger than the original data size. Base64 encoded data is consistently about 35% larger than the original data, whereas percent encoding could be as little as 0% or as much as 200% larger depending on the data. For binary formats such as images, base64 will almost always result in a smaller output than percent encoding.

Usage in HTML

To use a data URI in an HTML file, simply inject it in the src attribute of a tag such as <img> or <iframe>. Continuing with the copy example, your HTML might look like the following:

<img alt="copy" src="data:image/png;base64,
    iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAIp
    ...truncated for readability...
    Vv7atFglaFIu5KLuPxeX/xsp7aR6AAAAAElFTkSuQmCC
" />

Newlines in the URI are generally ignored, so insert breaking newlines wherever you feel is appropriate. In the above examples, the input has been truncated to 120 characters per line.

Usage in CSS

Using data URIs in CSS files is nearly the same as in HTML. Continuing the above example, you might have:

.copy {
  background: url(data:image/png;base64,
    iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAIp
    ...truncated again for readability...
    Vv7atFglaFIu5KLuPxeX/xsp7aR6AAAAAElFTkSuQmCC
  );
}

Converting files to base64 encoded data

Here's a simple PHP script to help you convert your files to either base64 or percent encoded strings:

encode.php
<?php
 
if ($_FILES && $_FILES['uploadfile']) {
    header( "Content-type: text/plain; charset=UTF-8" );
    $fp = fopen( $_FILES['uploadfile']['tmp_name'], 'rb' );
    if ($_POST['base64']) {
        while (!feof($fp)) echo(base64_encode(fread($fp, 45))."\n");
    } else {
        $buf = '';
        while (!feof($fp)) {
            $buf .= rawurlencode(fread($fp, 120));
            while (strlen($buf)>=60) {
                echo(substr($buf,0,60)."\n");
                $buf = substr($buf,60);
            }
        }
        if ($buf) echo($buf);
    }
    fclose($fp);
    die();
}
 
?>
<html>
<head>
<title>Base64 File Encoder</title>
</head>
<body>
<form action="" method="post" enctype="multipart/form-data">
<p><label>File to convert: <input type="file" name="uploadfile" /></label>
<p><label>Check here to use base64 encoding: <input type="checkbox" name="base64" checked="checked" /></p>
<p><input type="submit" name="submit" value="Submit" /></p>
</form>
</body>
</html>

Copy the above to a file called encode.php and host it on your own private server (localhost is fine). When you access the page, it will show you an upload form where you can post your files and get back the encoded data.

Enjoy! As always I'll be happy to answer any questions.


Got something to say?

Leave a comment
Sorry, comments are disabled.

or, read what others have said...