Загрузить файл
  1. base64hub
  2. Base64 в коде
  3. Python
Код

Base64 в Python

Модуль base64 из стандартной библиотеки покрывает всё: стандартный и URL-safe алфавиты, файлы и строки MIME. Каждый фрагмент ниже запущен на Python 3.13.

Закодировать и декодировать строку

Функции Base64 в Python работают с байтами, поэтому текст сначала нужно перевести в UTF-8, а в конце декодировать обратно. Пропустить один из этих шагов – самая частая ошибка.

import base64

s = "héllo wörld"
encoded = base64.b64encode(s.encode("utf-8")).decode("ascii")
print(encoded)                                    # aMOpbGxvIHfDtnJsZA==

decoded = base64.b64decode(encoded).decode("utf-8")
print(decoded)                                    # héllo wörld

b64decode принимает и str, и bytes, а b64encode – только байтоподобные объекты. Если передать строку, получите TypeError: a bytes-like object is required, not 'str'.

Чтобы проверить результат вручную, вставьте его в декодер Base64: он работает локально в браузере.

URL-safe Base64 с дополнением и без

URL-safe алфавит заменяет + на -, а / на _ (см. Base64 и Base64URL). Python сохраняет дополнение =; если целевой формат (JWT, многие API) обходится без него, отрежьте его сами.

data = b"<<???>>"
print(base64.b64encode(data))                     # b'PDw/Pz8+Pg=='
print(base64.urlsafe_b64encode(data))             # b'PDw_Pz8-Pg=='
print(base64.urlsafe_b64encode(data).rstrip(b"="))  # b'PDw_Pz8-Pg'

Чтобы декодировать строку без дополнения, его нужно вернуть, потому что Python требует длину, кратную 4:

def b64url_decode(s: str) -> bytes:
    return base64.urlsafe_b64decode(s + "=" * (-len(s) % 4))

print(b64url_decode("PDw_Pz8-Pg"))                # b'<<???>>'

b64encode и b64decode принимают также аргумент altchars – двухбайтовую строку, которая заменяет +/. altchars=b"-_" равносилен URL-safe функциям.

print(base64.b64decode("PDw_Pz8-Pg==", altchars="-_"))  # b'<<???>>'

Закодировать и декодировать файл

Если файл помещается в память, прочитайте байты и закодируйте их одним вызовом:

from pathlib import Path

b64 = base64.b64encode(Path("hello.txt").read_bytes()).decode("ascii")
print(b64)                                        # aGVsbG8K
Path("hello-copy.txt").write_bytes(base64.b64decode(b64))

Если нужно разово что-то сконвертировать без всякого кода, то же самое в браузере делает инструмент файл в Base64.

Большие файлы и потоки

Base64 превращает каждые 3 входных байта в 4 символа, так что поток можно кодировать по частям, если каждая часть, кроме последней, кратна 3 байтам. Тогда дополнение не появится в середине, а склеенный вывод совпадёт с кодированием за один вызов.

CHUNK = 3 * 1024 * 256  # 768 KiB, a multiple of 3

with open("big.bin", "rb") as src, open("big.b64", "wb") as dst:
    while chunk := src.read(CHUNK):
        dst.write(base64.b64encode(chunk))

Мы сверили это с base64 -w 0 на случайном файле в 100 КБ: совпадение байт в байт. Декодирование по частям работает так же с размером частей, кратным 4 символам, если во входных данных нет переносов строк.

Перенос строк MIME

Почта и некоторые старые системы требуют строк не длиннее 76 символов (RFC 2045). Так делает encodebytes, а base64.encode / base64.decode делают то же между файловыми объектами:

print(base64.encodebytes(b"x" * 60))
# b'eHh4eHh4...eHh4\neHh4\n'   (76 chars, newline, rest, newline)

with open("big.bin", "rb") as src, open("big.mime", "wb") as dst:
    base64.encode(src, dst)
with open("big.mime", "rb") as src, open("big.out", "wb") as dst:
    base64.decode(src, dst)

Обратите внимание на перевод строки в конце: encodebytes всегда его добавляет. В браузере то же делает кодировщик MIME Base64.

Проверка входных данных

По умолчанию b64decode снисходителен: перед декодированием он выбрасывает все символы вне алфавита. Это скрывает ошибки. Передайте validate=True, чтобы сделать его строгим.

import binascii

print(base64.b64decode("aGk=!!"))                 # b'hi'  (junk ignored)

try:
    base64.b64decode("aGk=!!", validate=True)
except binascii.Error as e:
    print(e)                                      # Only base64 data is allowed

С validate=True переводы строк тоже считаются недопустимыми, так что сначала удалите переносы MIME. Все эти ошибки имеют тип binascii.Error (подкласс ValueError):

ВходСообщение
aGkIncorrect padding
aInvalid base64-encoded string: number of data characters (1) cannot be 1 more than a multiple of 4
aGk== с validate=TrueExcess padding not allowed
aGk=!! с validate=TrueOnly base64 data is allowed

Отчего возникает каждая из них, объясняет статья об ошибках Base64, а где ломается строка, покажет валидатор Base64.

Частые подводные камни

URL-safe строка в b64decode. Без validate=True символы - и _ молча выбрасываются, и вместо ошибки вы получаете мусор:

print(base64.b64decode("PDw_Pz8-Pg=="))           # b'<<\x0f\xcf\xc3\xe0'
print(base64.urlsafe_b64decode("PDw_Pz8-Pg=="))   # b'<<???>>'

urlsafe_b64decode принимает и стандартный Base64, так что, если вы не уверены, какой алфавит вам пришёл, безопаснее по умолчанию брать его.

Декодирование двоичных данных как текста. Если внутри картинка или ZIP, .decode("utf-8") падает с UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 0: invalid start byte. Оставьте данные байтами и запишите их в файл.

b'...' в выводе. Если напечатать результат b64encode, вы увидите литерал байтов. Если этот b' попал в JSON или заголовок, вы забыли .decode("ascii").

Base64 как защита. Это кодировка, обратить её может кто угодно. См. кодирование и шифрование.

Версии

  • base64.b64encode, b64decode, urlsafe_* и altchars есть во всех версиях Python 3.
  • b64decode принимает ASCII-строки str (а не только байты) начиная с Python 3.3.
  • encodestring и decodestring удалены в 3.9; используйте encodebytes и decodebytes.
  • binascii.b2a_base64(data, newline=False) (3.6+) – низкоуровневая функция, на которой построен b64encode; для b"hi" она возвращает b'aGk='.
  • В том же модуле есть b32encode, b16encode, b85encode и a85encode для других алфавитов.

Вопросы и ответы

Почему b64encode возвращает b'...', а не строку?

Функции Base64 в Python принимают байты и возвращают байты. Вызовите .decode("ascii") у результата, чтобы получить str, а у текста перед кодированием – .encode("utf-8").

Как в Python декодировать Base64 без дополнения?

Перед декодированием верните недостающие =: s + "=" * (-len(s) % 4). Строку без дополнения Python отвергает с ошибкой binascii.Error: Incorrect padding.

Чем b64decode отличается от urlsafe_b64decode?

urlsafe_b64decode перед декодированием заменяет - и _ на + и /. Обычный b64decode молча выбрасывает - и _ как мусор, если не передать validate=True, и вместо ошибки вы получаете неправильные байты.

Есть ли ещё base64.encodestring?

Нет. encodestring и decodestring удалены в Python 3.9. Для вывода в стиле MIME с переносами строк используйте encodebytes и decodebytes.

Читайте также