Enviar um arquivo
  1. base64hub
  2. Base64 no código
  3. Python
Código

Base64 em Python

O módulo base64 da biblioteca padrão cobre tudo: alfabetos padrão e URL-safe, arquivos e linhas MIME. Todos os trechos abaixo foram executados no Python 3.13.

Codificar e decodificar uma string

As funções de Base64 do Python trabalham com bytes, então o texto precisa ser codificado em UTF-8 antes e decodificado de volta no final. Esquecer um desses passos é o erro mais comum.

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

O b64decode aceita tanto str quanto bytes, mas o b64encode só aceita objetos do tipo bytes. Passar uma string gera TypeError: a bytes-like object is required, not 'str'.

Para conferir um resultado à mão, cole-o no decodificador Base64, que roda localmente no navegador.

Base64 URL-safe com e sem preenchimento

O alfabeto URL-safe troca + por - e / por _ (veja Base64 vs Base64URL). O Python mantém o preenchimento =; remova-o você mesmo se o formato de destino (JWT, muitas APIs) não o quiser.

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'

Para decodificar entrada sem preenchimento, é preciso recolocá-lo, porque o Python exige um comprimento múltiplo de 4:

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

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

O b64encode e o b64decode também aceitam o argumento altchars, uma string de dois bytes que substitui +/. altchars=b"-_" equivale às funções URL-safe.

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

Codificar e decodificar um arquivo

Para arquivos que cabem na memória, leia os bytes e codifique numa única chamada:

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))

Para uma conversão pontual sem escrever código, a ferramenta arquivo para Base64 faz o mesmo no navegador.

Arquivos grandes e streaming

O Base64 transforma cada 3 bytes de entrada em 4 caracteres de saída, então dá para codificar um fluxo em blocos, desde que cada bloco, exceto o último, tenha um múltiplo de 3 bytes. Assim nenhum preenchimento aparece no meio, e a saída concatenada é igual à de uma codificação numa só chamada.

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))

Comparamos isso com base64 -w 0 num arquivo aleatório de 100 KB: idêntico byte a byte. A decodificação em blocos funciona do mesmo jeito, com blocos de tamanho múltiplo de 4 caracteres, desde que a entrada não tenha quebras de linha.

Quebra de linha MIME

O e-mail e alguns sistemas legados querem linhas de no máximo 76 caracteres (RFC 2045). O encodebytes faz isso, e base64.encode / base64.decode fazem o mesmo entre objetos de arquivo:

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)

Repare na quebra de linha no final: o encodebytes sempre acrescenta uma. Para uma versão online, veja o codificador Base64 MIME.

Validar a entrada

Por padrão, o b64decode é tolerante: ele descarta qualquer caractere fora do alfabeto antes de decodificar. Isso esconde bugs. Passe validate=True para torná-lo rígido.

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

Com validate=True, quebras de linha também contam como inválidas, então remova antes as quebras de linha MIME. Os erros que você vai ver, todos do tipo binascii.Error (uma subclasse de ValueError):

EntradaMensagem
aGkIncorrect padding
aInvalid base64-encoded string: number of data characters (1) cannot be 1 more than a multiple of 4
aGk== com validate=TrueExcess padding not allowed
aGk=!! com validate=TrueOnly base64 data is allowed

O guia de erros de Base64 explica a causa de cada um, e o validador de Base64 mostra onde uma string quebra.

Armadilhas comuns

Entrada URL-safe passada ao b64decode. Sem validate=True, os caracteres - e _ são descartados em silêncio, e você recebe lixo em vez de um erro:

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

O urlsafe_b64decode também aceita entrada padrão, então é o padrão mais seguro quando você não sabe qual alfabeto recebeu.

Decodificar binário como texto. Se o payload é uma imagem ou um ZIP, .decode("utf-8") falha com UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 0: invalid start byte. Mantenha os bytes e grave-os num arquivo.

O b'...' na saída. Imprimir o retorno do b64encode mostra um literal de bytes. Se esse b' foi parar num JSON ou num header, faltou o .decode("ascii").

Tratar Base64 como proteção. É uma codificação, qualquer pessoa consegue revertê-la. Veja codificação vs criptografia.

Notas de versão

  • base64.b64encode, b64decode, urlsafe_* e altchars existem em todas as versões do Python 3.
  • O b64decode aceita entrada str ASCII (não só bytes) desde o Python 3.3.
  • encodestring e decodestring foram removidos no 3.9; use encodebytes e decodebytes.
  • binascii.b2a_base64(data, newline=False) (3.6+) é a função de baixo nível por trás do b64encode e retorna b'aGk=' para b"hi".
  • O mesmo módulo também tem b32encode, b16encode, b85encode e a85encode para outros alfabetos.

Perguntas frequentes

Por que o b64encode retorna b'...' em vez de uma string?

As funções de Base64 do Python recebem bytes e retornam bytes. Chame .decode("ascii") no resultado para obter uma str, e .encode("utf-8") no texto antes de codificá-lo.

Como decodificar Base64 sem preenchimento em Python?

Recoloque os = que faltam antes de decodificar: s + "=" * (-len(s) % 4). O Python recusa entrada sem preenchimento com binascii.Error: Incorrect padding.

Qual é a diferença entre b64decode e urlsafe_b64decode?

O urlsafe_b64decode troca - e _ por + e / antes de decodificar. O b64decode simples descarta - e _ em silêncio como lixo, então você recebe bytes errados em vez de um erro, a menos que passe validate=True.

O base64.encodestring ainda existe?

Não. encodestring e decodestring foram removidos no Python 3.9. Use encodebytes e decodebytes para saída no estilo MIME, com quebras de linha.

Continue lendo