Charger un fichier
  1. base64hub
  2. Base64 dans le code
  3. Python
Code

Base64 en Python

Le module base64 de la bibliothèque standard couvre tout : alphabets standard et URL-safe, fichiers et lignes MIME. Chaque extrait ci-dessous a été exécuté avec Python 3.13.

Encoder et décoder une chaîne

Les fonctions Base64 de Python travaillent sur des octets : le texte doit d’abord être encodé en UTF-8, puis décodé à la fin. Oublier l’une de ces étapes est l’erreur la plus courante.

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 accepte str comme bytes, mais b64encode ne prend que des objets de type bytes. Lui passer une chaîne lève TypeError: a bytes-like object is required, not 'str'.

Pour vérifier un résultat à la main, collez-le dans le décodeur Base64 : il fonctionne localement dans le navigateur.

Base64 URL-safe avec et sans remplissage

L’alphabet URL-safe remplace + par - et / par _ (voir Base64 ou Base64URL). Python conserve le remplissage = ; supprimez-le vous-même si le format cible (JWT, de nombreuses API) n’en veut pas.

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'

Pour décoder une entrée sans remplissage, il faut le remettre, car Python exige une longueur multiple de 4 :

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

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

b64encode et b64decode acceptent aussi un argument altchars, une chaîne de deux octets qui remplace +/. altchars=b"-_" équivaut aux fonctions URL-safe.

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

Encoder et décoder un fichier

Pour les fichiers qui tiennent en mémoire, lisez les octets et encodez-les en un seul appel :

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

Pour une conversion ponctuelle sans écrire de code, l’outil fichier en Base64 fait la même chose dans le navigateur.

Gros fichiers et flux

Base64 transforme chaque groupe de 3 octets d’entrée en 4 caractères de sortie : vous pouvez donc encoder un flux par morceaux, à condition que chaque morceau sauf le dernier soit un multiple de 3 octets. Aucun remplissage n’apparaît alors au milieu, et la sortie concaténée est identique à celle d’un encodage en un seul appel.

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

Nous l’avons comparé à base64 -w 0 sur un fichier aléatoire de 100 Ko : identique octet pour octet. Le décodage par morceaux fonctionne de la même façon avec des tailles multiples de 4 caractères, à condition que l’entrée ne contienne pas de sauts de ligne.

Découpage des lignes MIME

L’e-mail et certains systèmes anciens veulent des lignes de 76 caractères au plus (RFC 2045). encodebytes le fait, et base64.encode / base64.decode le font entre objets fichier :

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)

Notez le saut de ligne final : encodebytes en ajoute toujours un. Pour une version en ligne, voir l’encodeur Base64 MIME.

Valider une entrée

Par défaut, b64decode est tolérant : il jette tout caractère hors de l’alphabet avant de décoder. Cela masque des bugs. Passez validate=True pour le rendre strict.

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

Avec validate=True, les sauts de ligne comptent aussi comme invalides : supprimez d’abord les retours à la ligne MIME. Voici les erreurs que vous verrez, toutes de type binascii.Error (une sous-classe de ValueError) :

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

Le guide des erreurs Base64 explique la cause de chacune, et le validateur Base64 montre où une chaîne casse.

Pièges courants

Une entrée URL-safe passée à b64decode. Sans validate=True, les caractères - et _ sont supprimés en silence, et vous obtenez des octets sans queue ni tête au lieu d’une erreur :

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

urlsafe_b64decode accepte aussi l’entrée standard : c’est le choix par défaut le plus sûr quand vous ne savez pas quel alphabet vous avez reçu.

Décoder du binaire comme du texte. Si le contenu est une image ou un ZIP, .decode("utf-8") échoue avec UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 0: invalid start byte. Gardez-le en octets et écrivez-le dans un fichier.

Le b'...' dans la sortie. Afficher la valeur de retour de b64encode montre un littéral bytes. Si ce b' se retrouve dans du JSON ou un en-tête, vous avez oublié .decode("ascii").

Considérer Base64 comme une protection. C’est un encodage, et n’importe qui peut l’inverser. Voir encodage ou chiffrement.

Notes de version

  • base64.b64encode, b64decode, urlsafe_* et altchars existent dans toutes les versions de Python 3.
  • b64decode accepte une entrée str ASCII (et pas seulement des octets) depuis Python 3.3.
  • encodestring et decodestring ont été supprimés en 3.9 ; utilisez encodebytes et decodebytes.
  • binascii.b2a_base64(data, newline=False) (3.6+) est la fonction de bas niveau derrière b64encode et renvoie b'aGk=' pour b"hi".
  • Le même module propose aussi b32encode, b16encode, b85encode et a85encode pour d’autres alphabets.

FAQ

Pourquoi b64encode renvoie-t-il b'...' au lieu d'une chaîne ?

En Python, les fonctions Base64 prennent des octets et renvoient des octets. Appelez .decode("ascii") sur le résultat pour obtenir une str, et .encode("utf-8") sur le texte avant de l’encoder.

Comment décoder en Python du Base64 sans remplissage ?

Remettez les = manquants avant de décoder : s + "=" * (-len(s) % 4). Python refuse une entrée sans remplissage avec binascii.Error: Incorrect padding.

Quelle est la différence entre b64decode et urlsafe_b64decode ?

urlsafe_b64decode convertit - et _ en + et / avant de décoder. Le simple b64decode supprime sans rien dire - et _, considérés comme des déchets : vous obtenez donc des octets erronés au lieu d’une erreur, sauf si vous passez validate=True.

base64.encodestring est-il toujours disponible ?

Non. encodestring et decodestring ont été supprimés dans Python 3.9. Utilisez encodebytes et decodebytes pour une sortie de style MIME avec des sauts de ligne.

Continuer la lecture