Как декодировать строку Base64 обратно в картинку
Что на самом деле лежит внутри Base64-строки изображения, как вернуть её в PNG или JPG и о какой прибавке к весу обычно молчат.
Открываете JSON-ответ, исходник письма или чужой CSS, а там стена из букв и цифр на три тысячи символов, начинается с iVBORw0KGgo. Это картинка. Просто вы её пока не видите.
Инструмент Base64 в изображение превращает эту стену обратно в то, на что можно посмотреть и что можно сохранить.
Что это за строка
Base64 переписывает произвольные байты 64 печатными символами, чтобы двоичные данные пережили путешествие там, где допустим только текст. Тело письма. Поле JSON. Атрибут в HTML. Каждые 3 байта исходного файла превращаются в 4 символа — отсюда и берётся прибавка примерно в 33%.
Формат выдают первые символы, потому что сигнатура файла всегда оказывается в одном и том же месте:
iVBORw0KGgo— это PNG/9j/— JPEGR0lGOD— GIFUklGR— WebPPHN2ZwилиPD94bWw— SVG (это XML, поэтому начало читается глазами)
Знание этих четырёх префиксов сэкономило мне больше времени на отладке, чем хочется признавать. Когда коллега клянётся, что API отдаёт JPEG, а строка начинается с iVBOR, спор закончен.
Как декодировать
Вставьте строку в поле. Можно отдать целиком data:image/png;base64,..., можно только полезную нагрузку после запятой — сработает и то и другое. Если вы скопировали значение из стилей вместе с обёрткой url("..."), она тоже отрежется.
Появится превью, а рядом — формат, размер файла и разрешение в пикселях. Дальше выбираете, что скачать: исходные байты или перекодировку в PNG либо JPG.
Одна деталь про JPG, на которой спотыкаются. В JPEG нет альфа-канала. Если перевести PNG-логотип с прозрачным фоном напрямую в JPG, прозрачные пиксели обязаны стать чем-то, и наивный ответ — чёрным. Инструмент подкладывает белый фон перед кодированием, обычно этого и хотят. Но если прозрачность нужна по-настоящему, оставайтесь на PNG.
Почему формат определяется по байтам, а не по подписи
MIME-тип в data URI — это просто надпись, которую кто-то напечатал. Она бывает неверной, а в сгенерированном коде неверна довольно часто. Поэтому декодер читает настоящую сигнатуру из декодированных байтов и сообщает, что перед вами на самом деле. JPEG, подписанный как image/png, всё равно раскодируется и корректно отрисуется.
Когда Base64-картинка — плохая идея
Встроить изображение прямо в разметку кажется аккуратным: меньше HTTP-запросов, не надо выкладывать отдельный файл. Для иконки в 400 байт нормально. После пары килобайт начинаются издержки:
- Те самые 33% оплачиваются при каждой загрузке страницы, а встроенный data URI лежит внутри HTML или CSS и не кэшируется отдельно от них
- Браузер не может отложить загрузку картинки, которая уже находится в разметке
- Base64 сжимается gzip заметно хуже исходного бинарника
- Фотография на 200 КБ становится строкой на 267 КБ, и теперь ваш CSS тяжелее на эти 267 КБ для каждого посетителя
Правило, которого держусь: меньше 2 КБ — встраивайте, больше — отдавайте настоящим файлом.
Приватность
Строка не покидает браузер. Декодирование, определение формата, превью и перекодировка выполняются на вашей машине. Это важно, когда на картинке внутренняя схема, подписанный документ или неопубликованная работа клиента.
Есть строка, которую не получается прочитать? Вставьте её в декодер и посмотрите, что это.