Conversion d'images en Base64 avec Canvas
Lorsque vous devez exporter des données d’image depuis le navigateur—que ce soit pour des aperçus côté client, l’intégration de petits graphiques ou le traitement d’images avant téléversement—l’API HTML Canvas est l’outil le plus direct disponible. Cet article explique comment fonctionne la conversion canvas-vers-Base64 en JavaScript, quand l’utiliser et où se trouvent les pièges courants.
Points clés à retenir
- La méthode
toDataURL()du canvas retourne un URI de données complet (avec préfixe MIME), et non une chaîne Base64 brute. Supprimez le préfixe lorsque vous n’avez besoin que des données encodées. - Privilégiez
toBlob()plutôt quetoDataURL()pour les téléversements de fichiers et les images volumineuses, car cette méthode est asynchrone et plus efficace en termes de mémoire. - PNG est le seul format de sortie universellement pris en charge. La prise en charge de JPEG et WebP varie selon les navigateurs.
- Les images cross-origin contamineront le canvas sauf si le serveur envoie les en-têtes CORS appropriés et que vous définissez
crossOrigin = 'anonymous'avant d’assigner lesrcde l’image.
Le flux de travail de base
La conversion d’une image en Base64 à l’aide d’un canvas suit trois étapes : charger l’image, la dessiner sur un élément canvas, puis exporter les données du canvas sous forme de chaîne encodée.
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
const image = new Image();
image.onload = function () {
canvas.width = image.width;
canvas.height = image.height;
ctx.drawImage(image, 0, 0);
const dataURL = canvas.toDataURL('image/png');
console.log(dataURL); // "data:image/png;base64,iVBORw0KGg..."
};
image.src = '/path/to/local-image.jpg';
Ceci constitue la base de l’encodage d’images basé sur canvas. La méthode toDataURL() retourne un URI de données complet, et non une chaîne Base64 brute.
Data URL vs. chaîne Base64 brute
Comprendre la différence est important. canvas.toDataURL() retourne une chaîne dans ce format :
data:image/png;base64,iVBORw0KGg...
Elle inclut un préfixe de type MIME, l’étiquette d’encodage, puis les données Base64 proprement dites. Si vous n’avez besoin que de la portion Base64 brute—par exemple, pour l’envoyer en JSON à un serveur—supprimez le préfixe :
const base64String = canvas.toDataURL('image/jpeg').split(',')[1];
Cela divise la chaîne à la première virgule et prend tout ce qui suit. Le préfixe est supprimé et les données encodées brutes restent.
toDataURL vs. toBlob : laquelle choisir ?
C’est la décision que la plupart des articles passent sous silence. Voici une comparaison directe :
| Caractéristique | toDataURL() | toBlob() |
|---|---|---|
| Type de retour | String (synchrone) | Blob (asynchrone, basé sur callback) |
| Utilisation mémoire | Plus élevée (Base64 ajoute ~33% de surcharge) | Plus faible (représentation binaire) |
| Idéal pour | Petites images, intégrations rapides | Téléversements, images volumineuses |
toDataURL() est synchrone et pratique, mais elle charge l’intégralité de la chaîne encodée en mémoire d’un coup. L’encodage Base64 augmente la taille des données d’environ 33% par rapport au binaire original. Pour les images volumineuses, cela compte.
toBlob() est asynchrone et produit directement un Blob binaire, ce qui est plus efficace pour les téléversements :
canvas.toBlob((blob) => {
const formData = new FormData();
formData.append('image', blob, 'export.jpg');
fetch('/upload', { method: 'POST', body: formData });
}, 'image/jpeg', 0.85);
N’utilisez Base64 que lorsque vous avez spécifiquement besoin d’une chaîne—pour l’intégration dans du JSON, le stockage dans un champ texte ou la génération d’un URI de données pour une balise <img>. Pour les téléversements de fichiers, toBlob() est le meilleur choix.
Discover how at OpenReplay.com.
Prise en charge des formats et paramètre de qualité
PNG est le seul format de sortie garanti sur tous les navigateurs. La prise en charge de JPEG et WebP dépend de l’environnement :
canvas.toDataURL('image/jpeg', 0.85); // qualité : 0 à 1, JPEG/WebP uniquement
canvas.toDataURL('image/webp', 0.9); // non pris en charge par tous les navigateurs
Le paramètre de qualité n’a aucun effet sur PNG, qui est toujours sans perte. Pour JPEG, 0.85 est une valeur par défaut raisonnable qui équilibre taille de fichier et qualité visuelle. Si vous passez un format non pris en charge, le navigateur bascule silencieusement vers PNG.
Le problème du canvas contaminé
Si vous dessinez une image cross-origin sur un canvas sans configuration CORS appropriée, le canvas devient « contaminé ». Appeler toDataURL() ou toBlob() sur un canvas contaminé génère une SecurityError.
Pour éviter cela, le serveur hébergeant l’image doit envoyer les en-têtes CORS appropriés (Access-Control-Allow-Origin), et vous devez définir crossOrigin sur l’élément image avant d’assigner son src :
const image = new Image();
image.crossOrigin = 'anonymous';
image.src = 'https://other-origin.com/image.png';
L’ordre ici est critique. Définir crossOrigin après src peut amener le navigateur à commencer le chargement sans le flag CORS, ce qui contamine quand même le canvas. Les images chargées depuis la même origine ne posent jamais problème. Les images cross-origin sans en-têtes CORS ne peuvent pas être exportées—il n’existe aucune solution de contournement côté client.
Quand l’encodage basé sur Canvas a du sens
Utilisez l’approche canvas lorsque vous devez :
- Redimensionner ou recadrer une image côté client avant l’encodage
- Appliquer des filtres ou transformations avant l’exportation
- Intégrer de petites images traitées comme URI de données
Si vous devez seulement lire un fichier local en Base64 sans manipulation canvas, FileReader.readAsDataURL() est plus simple et évite complètement le canvas.
Conclusion
L’API Canvas vous donne un contrôle précis sur l’encodage d’images dans le navigateur. Utilisez toDataURL() pour les petites images et les cas d’usage basés sur des chaînes, privilégiez toBlob() pour les téléversements et le travail sensible aux performances, et tenez toujours compte de CORS lorsque vous travaillez avec des images externes. Lorsqu’aucune manipulation de pixels n’est requise, évitez complètement le canvas et utilisez plutôt FileReader.
FAQ
Oui, vous pouvez dessiner un SVG sur un canvas et appeler toDataURL(). Cependant, le SVG doit d'abord être chargé comme élément Image, et les restrictions cross-origin s'appliquent toujours. Le résultat exporté est une image bitmap rastérisée, pas un vecteur évolutif. Toutes les ressources externes référencées par le SVG, telles que les polices ou images liées, peuvent ne pas s'afficher correctement sur le canvas.
Cela se produit généralement parce que toDataURL() est appelée avant que l'image n'ait fini de se charger. Assurez-vous de l'appeler à l'intérieur du gestionnaire onload de l'image. Une image noire peut également résulter du fait de ne pas définir la largeur et la hauteur du canvas pour correspondre aux dimensions de l'image source avant de dessiner, ce qui laisse le canvas à sa taille par défaut de 300 par 150 pixels.
Il n'y a pas de limite formelle dans la spécification, mais les navigateurs imposent des contraintes pratiques. Les très grands canvas peuvent produire des chaînes Base64 qui consomment une mémoire importante ou provoquent le ralentissement ou le plantage de l'onglet du navigateur. Pour les images volumineuses, toBlob() est l'alternative plus sûre et plus efficace en mémoire.
Les éléments canvas standard ne sont pas disponibles dans les Web Workers car ils dépendent du DOM. Cependant, vous pouvez utiliser OffscreenCanvas, qui est pris en charge par les navigateurs modernes, pour effectuer le dessin et appeler sa méthode convertToBlob() à l'intérieur d'un worker. Notez qu'OffscreenCanvas ne prend pas en charge toDataURL(), vous devriez donc convertir le Blob résultant en Base64 à l'aide d'un FileReader si une chaîne est requise.
Complete picture for complete understanding
Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.
Check our GitHub repo and join the thousands of developers in our community.