Генератор Data URI

Закодируйте изображение, шрифт или текст в data: URI или раскодируйте его — с измерением Base64 против процентного кодирования. Работает в браузере.

Источник
или перетащите сюда — он читается локально и никуда не загружается
Результат
data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20fill='none'%20stroke='%232563eb'%20stroke-width='2'%3E%3Ccircle%20cx='12'%20cy='12'%20r='9'/%3E%3Cpath%20d='M8%2012.5l2.5%202.5%205-6'/%3E%3C/svg%3E

Здесь короче процентное кодирование: 255 символов против 272, экономия 17.

Размеры

Байт в источнике
174
URI, Base64
272
URI, процентное кодирование
255

Файл внутри адреса

Data URI — это целый файл, записанный как адрес. Вместо ссылки на ресурс, за которым браузеру нужно сходить, он несёт сами байты, так что иконка может жить внутри таблицы стилей, которая её использует, а маленькое изображение — внутри HTML, который его показывает. RFC 2397 описал эту схему в 1998 году, и она поддерживается везде уже два десятилетия.

Грамматика короткая. После имени схемы идёт необязательный тип содержимого с необязательными параметрами, затем необязательный флаг base64, затем запятая, затем данные. Всё до запятой — заголовок, всё после — полезная нагрузка. Именно поэтому браузер делит строку по первой запятой и поэтому запятая внутри данных безвредна.

data:[<mediatype>][;base64],<data>

data:,hello                          text/plain;charset=US-ASCII
data:text/plain;charset=utf-8,hello  the same bytes, spelled out
data:image/png;base64,iVBORw0KGgo=   binary, Base64 encoded
data:image/svg+xml,%3Csvg%20...      markup, percent-encoded

Пустой заголовок допустим и означает text/plain;charset=US-ASCII — единственное значение по умолчанию, которое даёт спецификация. Эта страница сообщает, что применила его, а не показывает тип, который вы никогда не писали.

Два кодирования, и какое короче

RFC описывает два способа записать полезную нагрузку, и выбор между ними — не дело вкуса: для конкретного файла один из них заметно короче, а какой именно, целиком зависит от содержимого файла.

Base64 переписывает каждые три байта как четыре символа из алфавита в 64 символа. Цена фиксированная и предсказуемая: ровно на треть больше плюс дополнение. Он работает с чем угодно, поэтому к нему тянутся по умолчанию.

Процентное кодирование оставляет любой допустимый в адресе символ точно как есть и тратит три символа на каждый недопустимый. Для данных, состоящих в основном из простого ASCII — SVG, небольшой таблицы стилей, куска JSON, — большинство байтов проходит нетронутыми, и результат выходит короче Base64, часто на двадцать-тридцать процентов. Для PNG или шрифта, где экранировать приходится почти каждый байт, длина приближается к тройной от исходной и оказывается намного хуже Base64.

Сколько именно экранировать — тоже решение, и эта страница предлагает оба ответа:

  • Строгий набор экранирует каждый байт вне незарезервированных символов RFC 3986 — букв, цифр, дефиса, точки, подчёркивания и тильды. Результат безопасен в любом контексте ценой экранирования знаков препинания, которые в этом не нуждались.
  • Минимальный набор сохраняет всё, что грамматика data URI действительно допускает, включая косые черты, двоеточия, знаки равенства, кавычки и скобки, которыми полна разметка. Именно здесь процентное кодирование выигрывает, и именно поэтому SVG с одинарными кавычками вокруг атрибутов кодируется намного лучше, чем SVG с двойными.
  • Назначение добавляет ещё одно правило. Внутри HTML-атрибута голый амперсанд начинает ссылку на символ, поэтому выбор назначения img экранирует и его. Больше ничего не меняется: двойная кавычка, угловые скобки и обратная косая черта и так вне разрешённого набора и экранируются на обоих уровнях.
  • Знак процента экранируется всегда, на любом уровне. Он открывает escape-последовательность, поэтому буквальный процент нужно писать как %25, иначе два следующих символа читаются как шестнадцатеричные.

Чтобы не гадать, страница кодирует данные обоими способами и показывает обе длины и разницу. Берите победителя или задайте кодирование вручную, если у вас есть причина.

Эти 33% — число до сжатия

Почти всякий разговор про data URI повторяет, что Base64 добавляет треть к размеру файла. Арифметически это верно, а практически вводит в заблуждение, потому что число описывает байты до того, как они попадут в сеть, а любой сервер сжимает текст на выходе.

Base64 не добавляет информации; он размазывает ту же информацию по большему числу символов, используя лишь 64 из 256 значений, которые может принимать байт. Это ровно та избыточность, ради удаления которой и существуют gzip и Brotli. Для уже сжатых данных — PNG, JPEG, шрифта WOFF2 — компрессор не может тронуть сами данные, но может упаковать Base64 обратно почти до исходного размера. Знаменитая треть по большей части исчезает.

Поэтому эта страница измеряет, а не утверждает. Она сжимает три вещи собственной реализацией gzip в браузере: данные так, как их отдавали бы отдельным файлом; документ, несущий их в Base64; и документ, несущий их в процентном кодировании. Эти три числа — честная версия аргумента о размере, и они часто показывают, что настоящая цена встраивания вовсе не размер.

Настоящие издержки в другом. Встроенный файл нельзя закэшировать отдельно, поэтому он скачивается заново с каждой копией документа, который его несёт, и никогда не будет общим для двух страниц. Он ещё и блокирует: таблица стилей не закончит разбор, пока не прочитает целиком тот URI, что сидит у неё посередине. HTTP/2 снял большую часть платы за отдельный запрос, из-за которой встраивание вообще стало привлекательным. Для крошечной иконки, которая есть на каждой странице, это по-прежнему разумный обмен; после нескольких десятков килобайт — обычно нет, и поэтому страница начинает предупреждать на тридцати.

Тип берётся из байтов

Data URI полезен, только если тип содержимого верен. Ошибитесь — и браузер откажется показать изображение, включит не тот разборщик или, для всего, что отдано с незнакомым ему типом, предложит скачивание.

Когда вы перетаскиваете файл, браузер сообщает собственный тип, но этот вывод сделан из расширения файла и ни из чего больше. Переименуйте JPEG в .png — и браузер назовёт его PNG. Эта страница вместо этого читает первые байты. Почти каждый двоичный формат начинается с сигнатуры: PNG — с байта, который не может встретиться в ASCII, а за ним буквы PNG; JPEG — с трёх фиксированных байтов; PDF — со знака процента и слова PDF; WOFF и WOFF2 — со своих четырёхсимвольных меток. Когда сигнатура и расширение расходятся, побеждает сигнатура, а расхождение отмечается.

Список намеренно короткий — форматы, которые люди действительно встраивают, а не всё, что умеет распознавать браузер. Если ничего не совпало, а байты — корректный текст, то тип текстовый, причём SVG и XML различаются по тому, чем открывается разметка. Если не совпало вообще ничего, ответ — application/octet-stream, и поле остаётся редактируемым, потому что неверная догадка хуже честного признания.

Для текстовых данных важна и кодировка. Умолчание спецификации — US-ASCII, а сегодня никто не имеет в виду именно это, поэтому текстовый тип предлагается с добавленным charset=utf-8. Пропустить его — не значит испортить байты, но это меняет то, как их прочитают обратно.

Прочитать его обратно

Вторая половина работы встречается чаще: вы нашли data: URI в таблице стилей, в дампе DOM или в сохранённой странице и хотите понять, что это. Вставьте его — и страница разберёт его на части: тип содержимого, его параметры, какое кодирование использовано, во сколько байтов он раскодируется против того, во сколько символов обходится, и на что похожи эти байты сами по себе, независимо от того, что заявляет заголовок. Изображение отрисовывается, текст показывается, всё остальное получает первые байты в шестнадцатеричном виде.

Разбор строгий, потому что вставленный URI — недоверенный ввод, а правдоподобный неверный ответ хуже ошибки. Отсутствующая запятая, испорченный тип содержимого, знак процента без двух шестнадцатеричных цифр, символ вне алфавита Base64 — каждый случай отклоняется с указанием позиции, а не чинится молча.

Две вещи допускаются, потому что их допускает любой браузер и потому что URI, скопированный из перенесённой по строкам таблицы стилей, иначе был бы непригоден. Пробелы внутри данных убираются, а недостающее дополнение Base64 добавляется. О том и о другом сообщается, чтобы вы знали: имеющийся у вас URI — не совсем тот, который работает везде.

Одну похожую вещь отклоняют сразу. URL-безопасный алфавит Base64, где плюс и косая черта заменены дефисом и подчёркиванием, используют JSON Web Token — и это не то, что принимает data URI. Принять его молча значило бы раскодировать в байты, отличные от браузерных, поэтому он отклоняется по имени, с позицией нарушающего символа.

Где это работает

Всё происходит в вашем браузере. Выбранный файл читается локальным файловым API и никуда не загружается; кодирование, измерение сжатия и разбор выполняются на вашей машине, и ничто не сохраняется и не журналируется. Здесь это важнее, чем на большинстве страниц: файлы, которые превращают в data URI, часто внутренние, а URI, которые вставляют для разбора, приходят с боевых страниц.

Частые вопросы

Мой файл куда-нибудь загружается?
Нет. Файл читается локально файловым API браузера, кодируется на вашей машине и никуда не отправляется. То же верно и для URI, который вы вставляете для разбора.
Что выбрать — Base64 или процентное кодирование?
То, что короче для ваших данных, а это страница измеряет за вас. Как правило: процентное кодирование для SVG, CSS, JSON и другого текста, Base64 для изображений, шрифтов и всего уже сжатого. Разница нередко составляет двадцать-тридцать процентов в ту или другую сторону.
Base64 правда увеличивает файл на 33%?
До сжатия — да: четыре символа на каждые три байта. В сети — по большей части нет. Base64 использует лишь 64 из 256 возможных значений байта, и именно эту избыточность убирает gzip, так что сжатый размер обычно близок к сжатому размеру исходного файла. Страница измеряет и то и другое, чтобы это было видно.
Насколько большим может быть data URI?
Современные браузеры не ставят жёсткого предела для URI внутри документа, хотя старые версии Internet Explorer ограничивали его 32 КБ. Размер — вопрос производительности, а не законности: встроенный файл нельзя закэшировать отдельно, и он скачивается заново с каждой копией документа. Эта страница кодирует до мегабайта и предупреждает выше тридцати килобайт.
Почему мой data URI с SVG ломается в CSS?
Почти всегда из-за неэкранированного символа. Решётка — например, из цвета #2563eb — начинает идентификатор фрагмента и обрезает URI в этом месте. Двойная кавычка закрывает строку CSS, внутри которой находится. Пишите SVG с одинарными кавычками вокруг атрибутов и используйте минимальный набор экранирования: он экранирует решётку и двойную кавычку, а всё остальное оставляет коротким.
Можно ли использовать data URI для скрипта или iframe?
Можно, но относитесь к этому как к вопросу безопасности, а не удобства. Data URI не наследует источник, и браузеры уже блокируют переход верхнего уровня на него именно поэтому. Политика безопасности контента, разрешающая data: в script-src или frame-src, отдаёт большую часть того, что политика и защищала; разрешение в img-src или font-src — дело обычное.
Почему мой URL-безопасный Base64 отклонён?
Потому что data URI принимает стандартный Base64. URL-безопасный алфавит меняет плюс и косую черту на дефис и подчёркивание — это другие символы, раскодирующиеся в другие байты. Принять их значило бы, что эта страница разбирает ваш URI не так, как браузер, который его действительно загрузит.
Браузер говорит один тип файла, а эта страница другой. Кто прав?
Страница. Браузер сообщает тип, выведенный из расширения, поэтому переименованный файл он определяет неверно. Эта страница читает сигнатуру в начале файла — то, что объявляет сам формат. Поле остаётся редактируемым, если у вас есть причина его переопределить.