Как перевести Base64 в GIF
- Вставьте Base64 или URI
data:image/gif;base64,...в поле либо откройте файл.txt, в котором он лежит. - Убедитесь, что в строке статуса написано Определено: GIF. Превью начнёт проигрывать анимацию, а над ним появится размер в пикселях.
- Нажмите Скачать файл, чтобы сохранить
.gif.
Ничего не перерисовывается и не пережимается. У сохранённого файла те же кадры, задержки, палитра и прозрачность, что и у закодированного GIF.
Это точно GIF, и он анимированный?
GIF начинается с GIF89a или GIF87a, в Base64 это R0lGODlh или R0lGODdh. Анимации, прозрачности и управлению повтором нужна более новая версия 89a, так что строка, начинающаяся с R0lGODdh, на практике – статичная картинка.
Двигается ли файл 89a, зависит от содержимого: один блок изображения в нём или много. Превью покажет это сразу. Чтобы присмотреться, откройте Показать hex-дамп. В анимированных GIF в столбце ASCII обычно виден текст NETSCAPE2.0: это блок с числом повторов. При небольшой таблице цветов он стоит в первых строках. У GIF с полной таблицей на 256 цветов сначала идут 768 байт палитры, и блок уезжает за пределы 512 байт, которые показывает дамп.
Если страница пишет «Похоже на WebP, а не на GIF» или называет MP4, «GIF» сконвертировали по дороге. Мессенджеры и сервисы поиска GIF часто отдают анимации в WebP или видео MP4, потому что они намного меньше. Сохраните файл с настоящим расширением кнопкой Скачать как, а если это MP4, откройте его на странице Base64 в видео.
Кадры, тайминг и повторы в коде
Чтобы разобрать GIF без редактора, достаточно Pillow:
import base64, io
from PIL import Image
def gif_info(b64: str) -> dict:
data = base64.b64decode(b64.split(",", 1)[-1])
with Image.open(io.BytesIO(data)) as im:
durations = []
for i in range(getattr(im, "n_frames", 1)):
im.seek(i)
durations.append(im.info.get("duration", 0))
return {
"size": im.size,
"frames": len(durations),
"total_ms": sum(durations),
"loop": im.info.get("loop"),
}
Для тестовой анимации из 3 кадров по 100 мс функция вернёт {'size': (20, 20), 'frames': 3, 'total_ms': 300, 'loop': 0}. loop, равный 0, означает «повторять бесконечно», а None – что в файле нет блока повтора и он проигрывается один раз. Работа с файлами подробнее описана в статье Base64 в Python.
Где встречаются GIF в Base64
- Индикаторы загрузки в CSS и HTML, встроенные как
data:image/gif;base64,.... Эта привычка осталась со времён, когда CSS-анимации и SVG ещё не были распространены. - Старые HTML-шаблоны писем и подписи со встроенными маленькими анимированными логотипами.
- Ответы API и экспорты чатов, где стикеры или реакции лежат в полях Base64.
- Пиксели-заглушки: GIF 1×1, который ставят как пустой
src. Он декодируется в изображение 1 × 1 px, и превью может показаться пустым. Прежде чем решить, что декодирование не удалось, посмотрите на размеры.
Размер GIF в Base64
GIF хранит каждый кадр со старой схемой сжатия, поэтому даже короткие анимации быстро дорастают до мегабайтов, а Base64 добавляет сверху ещё треть. Строка Размер показывает размер после декодирования: если «маленькая» строка GIF весит 5 MB, сам GIF – около 3,75 MB. Декодировать такой здесь можно, но встраивать его обратно в страницу – плохая идея. Если вы его переделываете, короткий зацикленный MP4 или WebM обычно в разы меньше. Закодировать GIF можно на странице GIF в Base64.
Вопросы и ответы
Почему декодированный GIF проигрывается только один раз?
Настройка повтора записана в самом файле. Если блока NETSCAPE2.0 нет или число повторов равно 1, браузеры проигрывают анимацию один раз и останавливаются на последнем кадре. Декодирование сохраняет то, что записано в файле; поменять это можно в редакторе GIF.
Как посчитать кадры в GIF из Base64?
Превью проигрывает GIF, но кадры не считает. В Python число даёт n_frames из Pillow, а сумма duration по кадрам – общую длительность; скрипт на этой странице считает и то, и другое.
Почему GIF останавливается на середине или последний кадр битый?
Base64 обрезан. Браузеры проигрывают дошедшие кадры и останавливаются там, где кончаются данные. Полный GIF заканчивается байтом-трейлером 3B, поэтому его Base64 обычно заканчивается на ADs=, Ow== или A7.