Encoder et Envoyer des Images en Base64
Mis à jour le 29 juillet 2026
Pourquoi utiliser le Base64 ?
Dans la leçon précédente, vous avez appris à envoyer des images par URL. Mais l’essentiel des images à traiter en entreprise n’a pas d’URL : une facture qui vient d’arriver par mail, une capture d’écran générée par un test automatisé, une photo prise par un opérateur sur une ligne de production. Publier ces fichiers sur un serveur accessible depuis Internet pour les faire analyser serait absurde, et souvent interdit.
C’est là qu’intervient l’encodage Base64. Il transforme les données binaires d’une image en une chaîne de texte que vous incluez directement dans votre requête API, sans URL externe et sans hébergement intermédiaire.
Quand utiliser URL vs Base64
| Critère | URL | Base64 |
|---|---|---|
| Images déjà hébergées | Idéal | Inutile |
| Images locales | Non adapté | Idéal |
| Images générées à la volée | Non adapté | Idéal |
| Latence réseau | Dépend du serveur | Aucune (intégré) |
| Taille de la requête | Légère | Plus lourde (~33%) |
| Sécurité/vie privée | URL publique | Données dans la requête |
La règle est simple : si l’image est accessible via une URL stable, utilisez l’URL. Sinon, utilisez le Base64.
Encoder une image en Base64
Aucune dépendance n’est nécessaire, Python fournit le module base64 dans sa bibliothèque standard :
import base64
def encoder_image(chemin_fichier):
"""Encode un fichier image en chaîne Base64."""
with open(chemin_fichier, "rb") as f:
return base64.standard_b64encode(f.read()).decode("utf-8")
# Encoder une image locale
image_b64 = encoder_image("captures/screenshot.png")
print(f"Longueur de la chaîne Base64 : {len(image_b64)} caractères")
Le "rb" à l’ouverture n’est pas optionnel : une image lue en mode texte lèvera une erreur de décodage. Attendez-vous à des chaînes longues — l’encodage augmente la taille d’environ 33 % par rapport au fichier original, une image de 1 Mo produisant environ 1,33 Mo de texte.
Format Data URI
La chaîne brute ne suffit pas : l’API doit savoir de quel format d’image il s’agit. Vous devez donc la présenter sous forme de data URI, qui préfixe les données par leur type MIME :
data:image/{format};base64,{donnees_base64}
Les quatre préfixes utilisables correspondent aux formats supportés :
data:image/jpeg;base64,...data:image/png;base64,...data:image/gif;base64,...data:image/webp;base64,...
Un PNG annoncé comme image/jpeg provoque une erreur de traitement ; d’où la fonction de détection dans l’exemple qui suit.
Exemple complet
Voici la chaîne complète, de la lecture du fichier local jusqu’à la réponse du modèle :
import base64
import os
from mistralai import Mistral
def encoder_image(chemin):
"""Encode une image locale en Base64."""
with open(chemin, "rb") as f:
return base64.standard_b64encode(f.read()).decode("utf-8")
# Déterminer le type MIME
def obtenir_mime(chemin):
"""Retourne le type MIME basé sur l'extension."""
extensions = {
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".png": "image/png",
".gif": "image/gif",
".webp": "image/webp"
}
ext = os.path.splitext(chemin)[1].lower()
return extensions.get(ext, "image/jpeg")
# Préparer l'image
chemin_image = "documents/facture-2024.png"
image_b64 = encoder_image(chemin_image)
mime_type = obtenir_mime(chemin_image)
data_uri = f"data:{mime_type};base64,{image_b64}"
# Appeler l'API
client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])
response = client.chat.complete(
model="mistral-small-latest",
messages=[
{
"role": "user",
"content": [
{"type": "text", "text": "Extrayez toutes les informations de cette facture."},
{"type": "image_url", "image_url": data_uri}
]
}
]
)
print(response.choices[0].message.content)
Le point à retenir : le champ s’appelle toujours image_url, même lorsqu’il contient un data URI. La structure du message ne change pas d’un iota par rapport à la leçon 4, seule la valeur diffère.
Encoder des images depuis d’autres sources
Le fichier sur disque n’est qu’un cas parmi d’autres. Si votre image existe déjà en mémoire, sous forme d’objet Pillow issu d’une capture ou d’un traitement, l’écrire sur disque pour la relire aussitôt serait un détour inutile — un buffer mémoire fait l’affaire :
import base64
from io import BytesIO
from PIL import Image
def capture_vers_base64(image_pil):
"""Convertit une image Pillow en data URI Base64."""
buffer = BytesIO()
image_pil.save(buffer, format="PNG")
b64 = base64.standard_b64encode(buffer.getvalue()).decode("utf-8")
return f"data:image/png;base64,{b64}"
# Exemple avec une image Pillow
img = Image.open("capture.png")
data_uri = capture_vers_base64(img)
Même logique pour une image récupérée par HTTP que vous ne souhaitez pas stocker, par exemple parce qu’elle provient d’un lien à durée de vie courte :
import base64
import requests
def url_vers_base64(url):
"""Télécharge une image et la convertit en data URI."""
response = requests.get(url, timeout=10)
response.raise_for_status()
content_type = response.headers.get("Content-Type", "image/jpeg")
b64 = base64.standard_b64encode(response.content).decode("utf-8")
return f"data:{content_type};base64,{b64}"
data_uri = url_vers_base64("https://exemple.com/photo.jpg")
Optimiser la taille avant encodage
Une photo de smartphone pèse aujourd’hui 4 à 8 Mo, soit près de 10 Mo une fois encodée. Elle sera de toute façon redimensionnée par l’API, comme vous l’avez vu en leçon 2 : vous auriez payé le transfert pour rien. Redimensionnez donc systématiquement avant d’encoder.
from PIL import Image
from io import BytesIO
import base64
def preparer_et_encoder(chemin, taille_max=1024, qualite=85):
"""Redimensionne, compresse et encode une image."""
img = Image.open(chemin)
# Redimensionner si nécessaire
if max(img.size) > taille_max:
img.thumbnail((taille_max, taille_max))
# Convertir en JPEG pour réduire la taille
buffer = BytesIO()
img.convert("RGB").save(buffer, format="JPEG", quality=qualite)
b64 = base64.standard_b64encode(buffer.getvalue()).decode("utf-8")
taille_ko = len(buffer.getvalue()) / 1024
print(f"Image encodée : {taille_ko:.0f} Ko")
return f"data:image/jpeg;base64,{b64}"
data_uri = preparer_et_encoder("photo_haute_resolution.png")
L’affichage de la taille finale n’est pas décoratif : gardez-le pendant vos essais, il vous dira immédiatement si votre pipeline envoie 80 Ko ou 8 Mo à chaque appel.
Limites à connaître
Trois contraintes accompagnent le Base64. L’image encodée s’ajoute au corps de la requête HTTP, si bien qu’au-delà de 10 Mo vous vous exposez à des timeouts. L’encodage charge par ailleurs l’image entière en mémoire : pour du traitement en batch, traitez les images une par une plutôt que de constituer une liste de mille chaînes encodées. Enfin, contrairement aux URL, les images Base64 sont retransmises intégralement à chaque requête — analyser deux fois la même facture, c’est l’envoyer deux fois.
Points clés à retenir
- Le Base64 est idéal pour les images locales ou générées dynamiquement
- Le format data URI combine le type MIME et les données :
data:image/png;base64,... - Redimensionnez et compressez vos images avant l’encodage pour optimiser la latence
- Le Base64 augmente la taille d’environ 33 % — gardez vos images sous 10 Mo
- Utilisez l’URL quand l’image est déjà hébergée, le Base64 dans tous les autres cas