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):
| Entrada | Mensagem |
|---|---|
aGk | Incorrect padding |
a | Invalid base64-encoded string: number of data characters (1) cannot be 1 more than a multiple of 4 |
aGk== com validate=True | Excess padding not allowed |
aGk=!! com validate=True | Only 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_*ealtcharsexistem em todas as versões do Python 3.- O
b64decodeaceita entradastrASCII (não só bytes) desde o Python 3.3. encodestringedecodestringforam removidos no 3.9; useencodebytesedecodebytes.binascii.b2a_base64(data, newline=False)(3.6+) é a função de baixo nível por trás dob64encodee retornab'aGk='parab"hi".- O mesmo módulo também tem
b32encode,b16encode,b85encodeea85encodepara 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.