Subir un archivo
  1. base64hub
  2. Base64 en código
  3. Python
Código

Base64 en Python

El módulo base64 de la biblioteca estándar lo cubre todo: alfabetos estándar y URL-safe, archivos y líneas MIME. Todos los fragmentos de esta página se ejecutaron con Python 3.13.

Codificar y decodificar una cadena

Las funciones Base64 de Python trabajan con bytes, así que el texto hay que codificarlo antes en UTF-8 y decodificarlo de nuevo al final. Olvidar cualquiera de los dos pasos es el error más habitual.

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 acepta tanto str como bytes, pero b64encode solo admite objetos de tipo bytes. Si le pasas una cadena, lanza TypeError: a bytes-like object is required, not 'str'.

Para comprobar un resultado a mano, pégalo en el decodificador Base64, que se ejecuta localmente en el navegador.

Base64 URL-safe con y sin relleno

El alfabeto URL-safe sustituye + por - y / por _ (consulta Base64 frente a Base64URL). Python conserva el relleno =; quítalo tú si el formato de destino (JWT, muchas API) no lo quiere.

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 una entrada sin relleno hay que volver a ponerlo, porque Python exige una longitud 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'<<???>>'

b64encode y b64decode también aceptan un argumento altchars, una cadena de dos bytes que sustituye a +/. altchars=b"-_" equivale a las funciones URL-safe.

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

Codificar y decodificar un archivo

Con archivos que caben en memoria, lee los bytes y codifícalos con una sola llamada:

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 una conversión puntual sin escribir código, la herramienta archivo a Base64 hace lo mismo en el navegador.

Archivos grandes y flujos

Base64 convierte cada 3 bytes de entrada en 4 caracteres de salida, así que puedes codificar un flujo por bloques siempre que cada bloque, salvo el último, sea múltiplo de 3 bytes. Así no aparece relleno en mitad de la salida y el resultado concatenado es igual al de codificarlo todo con una sola llamada.

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

Lo comprobamos frente a base64 -w 0 con un archivo aleatorio de 100 KB: idéntico byte a byte. Decodificar por bloques funciona igual con tamaños de bloque múltiplos de 4 caracteres, siempre que la entrada no tenga saltos de línea.

Cortes de línea MIME

El correo electrónico y algunos sistemas antiguos quieren líneas de 76 caracteres como máximo (RFC 2045). encodebytes hace eso, y base64.encode / base64.decode lo hacen entre objetos de archivo:

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)

Fíjate en el salto de línea final: encodebytes siempre añade uno. Para una versión online, consulta el codificador Base64 MIME.

Validar la entrada

Por defecto, b64decode es tolerante: desecha cualquier carácter fuera del alfabeto antes de decodificar. Eso esconde errores. Pasa validate=True para que sea estricto.

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

Con validate=True, los saltos de línea también cuentan como no válidos, así que quita antes los cortes de línea MIME. Los errores que verás, todos de tipo binascii.Error (una subclase de ValueError):

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

La guía de errores de Base64 explica qué provoca cada uno, y el validador Base64 muestra dónde se rompe una cadena.

Errores habituales

Entradas URL-safe pasadas a b64decode. Sin validate=True, los caracteres - y _ se descartan sin avisar, y obtienes basura en lugar de un error:

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

urlsafe_b64decode también acepta entradas estándar, así que es la opción más segura por defecto cuando no sabes qué alfabeto has recibido.

Decodificar binario como texto. Si el contenido es una imagen o un ZIP, .decode("utf-8") falla con UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 0: invalid start byte. Mantenlo como bytes y escríbelo en un archivo.

El b'...' en la salida. Al imprimir el valor que devuelve b64encode se ve un literal de bytes. Si ese b' acaba en un JSON o en una cabecera, te has olvidado de .decode("ascii").

Tratar Base64 como protección. Es una codificación y cualquiera puede revertirla. Consulta codificación frente a cifrado.

Notas de versión

  • base64.b64encode, b64decode, urlsafe_* y altchars existen en todas las versiones de Python 3.
  • b64decode acepta entradas str ASCII (no solo bytes) desde Python 3.3.
  • encodestring y decodestring se eliminaron en la 3.9; usa encodebytes y decodebytes.
  • binascii.b2a_base64(data, newline=False) (3.6+) es la función de bajo nivel que hay detrás de b64encode y devuelve b'aGk=' para b"hi".
  • El mismo módulo tiene también b32encode, b16encode, b85encode y a85encode para otros alfabetos.

Preguntas frecuentes

¿Por qué b64encode devuelve b'...' en lugar de una cadena?

Las funciones Base64 de Python reciben bytes y devuelven bytes. Llama a .decode("ascii") sobre el resultado para obtener un str, y a .encode("utf-8") sobre el texto antes de codificarlo.

¿Cómo decodifico en Python un Base64 que no tiene relleno?

Vuelve a añadir los = que faltan antes de decodificar: s + "=" * (-len(s) % 4). Python rechaza las entradas sin relleno con binascii.Error: Incorrect padding.

¿Qué diferencia hay entre b64decode y urlsafe_b64decode?

urlsafe_b64decode traduce - y _ a + y / antes de decodificar. El b64decode normal descarta en silencio - y _ como basura, así que obtienes bytes incorrectos en lugar de un error, salvo que pases validate=True.

¿Sigue existiendo base64.encodestring?

No. encodestring y decodestring se eliminaron en Python 3.9. Usa encodebytes y decodebytes para obtener una salida al estilo MIME con saltos de línea.

Sigue leyendo