Статьи

Перевод: Николай Мациевский aka sunnybear
Опубликована: 3 апреля 2008

Картинки в теле страницы с помощью data:URL

Примечание: ниже расположен перевод статьи "Inline Images with Data URLs", в которой рассматривается вопрос о внедрении картинки на веб-страницы при помощи data:URI. Эта схема позволяет вставить код картинок прямо в (X)HTML-страницу без обращений к внешним файлам, что уменьшает общее количество HTTP-обращений к серверу. Мои комментарии далее курсивом.

Встроенные (inline) изображения используют схему data:URI для внедрения прямо в тело веб-страницы. Как было определено в RFC 2397, такие URI предназначены для вставки небольших объектов как «непосредственные» данные. Такие объекты должны рассматриваться так же, как и любые другие внешние файлы. Использование встроенных изображений позволяет сэкономить HTTP-запросы к внешних ресурсах.

Поддержка браузерами data:URL

Хотя Opera 7.2+, Firefox, Safari, Netscape и Mozilla поддерживают data:URI, Internet Explorer 5–7 совсем нет. Однако, сообщается, что Internet Explorer 8 будет поддерживать эту схему, так как проходит Acid2 тест, что позволяет использовать data:URL как реальную альтернативу для внедрения небольших декоративных изображений. Существует также несколько приемов для поддержки старых версий Internet Explorer.

Схема data:URL

Вы уже имеете представление о других схемах URL в своих переходах по интернет-страницам, например, о http:, ftp: и mailto:. Схема data:URL предоставляет способ для внедрения «непосредственно данных» точно так же, как если бы они были подключены через вызовы внешних файлов. Синтаксис у data:URL следующий:

data:[<тип данных>][;base64],<данные>

В случае простых изображений вам нужно указать mime-тип для них (например, image/gif), за ним идет base64-представление бинарного файла с изображением. Ниже приведен пример (переводы строк добавлены, чтобы не разрывать страницу, на самом деле, их нет):

<img src="data:image/gif;base64,R0lGODlhEAAOALMAAOazToeHh0tLS/7LZv/0j
vb29t/f3//Ub//ge8WSLf/rhf/3kdbW1mxsbP//mf///yH5BAAAAAAALAAAAAAQAA4AAA
Re8L1Ekyky67QZ1hLnjM5UUde0ECwLJoExKcppV0aCcGCmTIHEIUEqjgaORCMxIC6e0Cc
guWw6aFjsVMkkIr7g77ZKPJjPZqIyd7sJAgVGoEGv2xsBxqNgYPj/gAwXEQA7" width="16" 
height="14" alt="внедренная иконка папки"/>

В результате вы получите следующее изображение иконки папки (частичный скриншот ниже):

иконка папки

CSS и встроенные изображения

Такие изображения, внедренные в HTML-страницы, не кешируются для повторного использования, и они не кешируются от странице к странице (это логично: ведь нам нужно каждый раз загрузить HTML-код для отображения этой картинки, они будут кешироваться только с HTML, их содержащим). Однако, CSS кешируется браузерами, и такие изображения могут быть повторно использованы вместе с использующим их селектором, например:

ul {list-style:none;}
ul > li {
    margin:0 0 .1em;
    background:url(data:image/gif;base64,R0lGODlhEAAOALMAAOazToeHh0tLS/7LZv/0j
    vb29t/f3//Ub//ge8WSLf/rhf/3kdbW1mxsbP//mf///yH5BAAAAAAALAAAAAAQAA4AAARe8L1
    Ekyky67QZ1hLnjM5UUde0ECwLJoExKcppV0aCcGCmTIHEIUEqjgaORCMxIC6e0CcguWw6aFjsV
    MkkIr7g77ZKPJjPZqIyd7sJAgVGoEGv2xsBxqNgYPj/gAwXEQA7) top left no-repeat;
    height:14px;
    text-indent:1.5em;
}
</style>

Теперь иконка папки будет повторяться для каждого вхождения LI (или можно также использовать соответствующий класс или ID).

<ul>
<li>Testing inline images, one</li>
<li>Two</li>
<li>Three</li>

</ul>

Что выглядит в Firefox примерно следующим образом (частичный скриншот):

иконки папки в списке при помощи css

Проблемы data:URL

С описанным выше подходом для подключения изображений связано две основные проблемы. Во-первых, вам нужно пересчитывать base64-представление изображений и редактировать CSS-файл каждый раз, когда само изображением меняется. Также IE до версии 7 включительно не поддерживает встроенных изображений. У первой проблемы есть простой решение на PHP:

<?php echo base64_encode(file_get_contents("../images/folder16.gif")) ?>

Этот код читает файл с изображением и автоматически преобразовывает его на сервере в base64. Однако, это простота этого решения повлечет некоторую дополнительную нагрузку на сервер. Как вариант можно рассмотреть автоматический пересчет всех картинок и вставка их в CSS-файл, например, раз в 5 минут по необходимости (если файл с изображением изменился). Дополнительно нужно будет озаботиться, чтобы сбросить кеширование для самого CSS-файла.

Работа в Internet Explorer

Существует два способа обойти отсутствие в IE поддержки data:URL. Используя распознавание браузеров (например, с помощью условных комментариев, ведь речь идет только про IE) можно просто отображать внешнее изображение для IE и встроенные изображения для остальных браузеров. Или вы можете использовать JavaScript для эмуляции этой поддержки в IE, но эта техника потребует довольно значительного объема JavaScript-кода. Вышеприведенный PHP-код позволяет легко вставить base64-аналог изображения (можно расширить этот пример, чтобы, например, распознавать заголовки, отправляемые браузером серверу и только для IE выводить URL для изображения, для остальных же кодировать его в base64):

ul {list-style:none;}
ul > li {
    margin:0 0 .1em;
    background: url(data:image/gif;base64,<?php 
	echo base64_encode(file_get_contents("../images/folder16.gif")) 
	?>) top left no-repeat;
    height:14px;
    text-indent:1.5em;
}

</style>

Когда ваш сервер анализирует CSS-файл, он автоматически перекодирует бинарный файл изображения в base64 и отправит эти данные внутри CSS-файла. Следующим шагом будет добавление распознавания браузеров для отправки изображения только IE, и встроенных изображений всем остальным. Это можно сделать либо внутри CSS-файла с PHP-кодом, либо с помощью условных комментариев, например:

<!--[if gte IE 5]>
<style type="text/css" src="ie.css">
<![endif]-->

<!--[if !(IE)]>
<style type="text/css" src="notie.css">

<![endif]-->

В файле ie.css должно быть нормальное обращение к картинке, например:

ul > li {
    margin:0 0 .1em;
    background: url(/images/folder16.gif) top left no-repeat;
}
...

Лично мне вышеприведенный код кажется сомнительным: все браузеры, кроме IE, посчитают, что CSS-файлы закомментированы и вообще к ним не обратятся. Поэтому я бы советовал оставить комментарии только для первого файла и переместить его вызов в конец, чтобы он переопределял общие стилевые правила.

Преимущества data:URL

С помощью data:URL вы экономите HTTP-запросы. Вместе с техникой CSS sprites data:URL могут значительно уменьшить число HTTP-запросов. Будет весьма интересно посмотреть, если удастся использовать data:URL в сочетании с USEMAPS или сделать CSS sprite с помощью data:URL. Итак, краткий список плюсов данного метода:

  • Экономят HTTP-запросы, предотвращают издержки, связанные с большим числом объектов.
  • Экономят число параллельных потоков: у браузеров есть ограничение (по спецификации HTTP/1.1, однако, Firefox, Opera и Safari несколько вольно его расценивают, в частности, позволяя настраивать этот параметр или значительно его увеличивая) на число одновременных соединений с одним хостом.
  • Упрощают HTTPS-запросы и улучшают производительность при таком типе соединения.

Недостатки data:URL

Встроенные изображения не поддерживаются в Internet Explorer 5–7, хотя сообщается, что версия 8 их поддерживает. Текстовое base64-представление данных также занимает больше, чем бинарное изображение. В наших тестах base64-данные были на 39–45% больше бинарного аналога, но gzip-сжатие позволяет уменьшить разницу до 89%. Предварительная оптимизация изображений перед base64-кодированием позволяет уменьшить их размер пропорционально.

Также существует ряд ограничений на размер встроенных изображений. От браузеров требуется поддерживать только URL длиной до 1024 байтов, в соответствие с вышеупомянутой спецификацией RFC. Однако, браузеры более либеральны к пользователям в том, что они принимают. Например, Opera ограничивает data:URL до примерно 4100 символов. Firefox поддерживает data:URL вплоть до 100Кб. В итоге, эта техника подходит больше для небольших по размеру изображений. Краткий список минусов:

  • Не поддерживается IE до 7 версии включительно.
  • Требуются дополнительные действия для обновления внедренного содержания (перекодировать, еще раз вставить).
  • Ограничена длина. Эта техника может быть полезна для вставки небольших, декоративных изображений.
  • Изображения, представленные в base64-кодировке, примерно на 33% больше размера их бинарного аналога.
  • Спасибо, посмотреть профиль zerkms, встроенные картинки (не в CSS) не получится закешировать по определению. Они будут кешироваться только с HTML-кодом.

Примеры data:URL

Ниже приведено несколько примеров, которые вы можете проверить в своем браузере. Все они отражают вышеприведенный код.

Дополнительные соображения по оптимизации

Наиболее разумным будет, мне кажется, подход, не увеличивающий общее число CSS-файлов, т.е. использующий характерные хаки для IE, чтобы только для него подключить фоновые изображения. Для IE версий 6 и ниже можно использовать * html, для IE 7, к сожалению, этот хак уже не работает, поэтому используем *+html (спасибо за дополнение Bueno). В итоге, вышеприведенный пример будет выглядеть примерно так:

* html ul li {
    background-image: url(/images/folder16.gif);
}
*+html ul li {
    background-image: url(/images/folder16.gif);
}

Также возможно автоматическое кодирование изображений, которые выводятся в base64, автоматически при изменении этих изображений (для этого потребуется простой скрипт, который проверяет, обновились ли соответствующие файлы, если обновились, то перезаписывает их представление в CSS-файле, заодно и меняет хеш-строку для подключения этого файла в HTML, чтобы избежать кеширования.

Для включения небольших графиков прямо в HTML-код прекрасно подойдут условные комментарии, когда для ряда браузеров изображение выводится в base64, а для остальные в виде IE подключается через условные комментарии. Если использовать связку относительное позиционирование родителя абсолютное позиционирование дочернего элемента, то IE будет просто выводить картинку из внешнего файла поверх непонятного (для него) объекта. Например, так:

<div id="chart">
    <img src="data:image/png;base64,..." alt="График" title="График"/>
    <!--[if IE]>
	<img src="chart.png" alt="График" title="График"/>
    <![endif]-->
</div>

Оба примера использования можно посмотреть на webo.in: на главной странице график выводится прямо в теге img, фоновый CSS sprite в CSS-файле записан также в base64-кодировке. Если у кого-то не отображается какая-либо картинка, напишите, пожалуйста, в комментариях будем думать над решением.

Хочу подчеркнуть, что решение об использовании data:URL должно приниматься на основе статистики использования браузеров для просмотра сайта (для webo.in доля IE составляет меньше 20%, что позволяет использовать более прогрессивные методы для оптимизации скорости загрузки).

Заключение

С релизом Internet Explorer 8 data:URI должны стать реальной альтернативой существующим методам вывода изображений. Вы можете вставить небольшие изображения прямо в тело страницы с помощью data:URL для экономии числа HTTP-запросов. data:URL является удобным способом для создания автономных веб-страниц, которым не требуются никакие внешние объекты для полного своего отображения.

Читать дальше

Все комментарии (habrahabr.ru)