Utiliser des images et canevas provenant d'autres origines
HTML permet d'utiliser l'attribut crossorigin sur les images. Utilisé avec un en-tête CORS adéquat, les images définies par l'élément <img> provenant d'origines étrangères pourront être utilisées au sein d'un élément <canvas> comme si elles avaient été chargées depuis l'origine courante.
Pour plus de détails sur l'attribut crossorigin, voir les attributs de paramétrage du CORS.
Canevas corrompu et sécurité
Les pixels composant un canevas pouvant venir de différentes sources, notamment d'images ou de vidéos récupérées depuis des hôtes tiers, il est nécessaire de se prémunir contre certains problèmes de sécurité.
Dès que des données sont chargées dans le canevas depuis une autre origine sans avoir été approuvées par le CORS, le canevas devient corrompu (tainted en anglais). Dès qu'un canevas est corrompu, il n'est plus considéré comme sécurisé et toute tentative de récupérer des données depuis les données de l'image résultera en une exception.
Si la source du contenu tiers est un élément HTML <img> ou SVG <svg>, il n'est plus permis de récupérer le contenu du canevas.
Si la source du contenu tiers est une image obtenue à partir d'un HTMLCanvasElement ou d'une ImageBitMap et que la source de l'image ne respecte pas les règles quant à l'unicité de l'origine, il ne sera pas possible de lire le contenu du canevas.
Appeler l'une des méthodes suivantes sur un canevas corrompu déclenchera une erreur :
- Appeler
getImageData()sur le contexte du canevas - Appeler
toBlob(),toDataURL()orcaptureStream()sur l'élément<canvas>lui-même
L'exception levée par de tels appels sera une exception SecurityError. Cette mesure protège les utilisateurs contre l'exposition de données privées via des images provenant de sites tiers sans permission.
Stocker une image provenant d'une origine tierce
Dans cet exemple, on souhaite autoriser la récupération et l'enregistrement d'images provenant d'une autre origine. Pour parvenir à ce résultat, il faudra configurer le serveur et également écrire du code pour le site web.
Configuration serveur
Pour commencer, configurons le serveur stockant les images avec un en-tête Access-Control-Allow-Origin qui permet un accès multi-origines aux fichiers images.
Dans la suite de cet exemple, on prendra le cas d'un site est servi via Apache. On pourra utiliser le fragment de configuration serveur Apache pour les images CORS, comme suit :
<IfModule mod_setenvif.c>
<IfModule mod_headers.c>
<FilesMatch "\.(avifs?|bmp|cur|gif|ico|jpe?g|jxl|a?png|svgz?|webp)$">
SetEnvIf Origin ":" IS_CORS
Header set Access-Control-Allow-Origin "*" env=IS_CORS
</FilesMatch>
</IfModule>
</IfModule>
Pour résumer, cela permet de configurer le serveur afin de pouvoir accéder aux fichiers graphiques (ceux avec les extensions ".bmp", ".cur", ".gif", ".ico", ".jpg", ".jpeg", ".png", ".svg", ".svgz" et ".webp") depuis d'autres origines, d'où qu'elles soient sur Internet.
Implémenter l'enregistrement
Maintenant que le serveur est configuré pour permettre la récupération d'image depuis plusieurs origines, on peut écrire le code qui permet à l'utilisateur·ice d'enregistrer les images en stockage local comme si elles étaient servies depuis le même domaine que le code.
Pour cela, on utilise l'attribut crossorigin en définissant crossOrigin sur l'élément HTMLImageElement sur lequel l'image sera chargée. Ainsi, on indique au navigateur de demander un accès multi-origine lors du téléchargement de l'image.
Démarrer le téléchargement
Voici le code qui démarre le téléchargement (déclenché par exemple lorsque l'utilisateur·ice clique sur un bouton « Télécharger ») :
function startDownload() {
let imageURL = "https://mdn.github.io/shared-assets/images/examples/mdn.svg";
let imageDescription = "Logo d'un dinosaure devant une carte";
downloadedImg = new Image();
downloadedImg.crossOrigin = "anonymous";
downloadedImg.addEventListener("load", imageReceived);
downloadedImg.alt = imageDescription;
downloadedImg.src = imageURL;
}
Ici, nous utilisons une URL codée en dur (imageURL) et un texte descriptif associé (imageDescription), mais cela pourrait venir de n'importe où. Pour commencer le téléchargement de l'image, nous créons un nouvel objet HTMLImageElement en utilisant le constructeur Image(). L'image est ensuite configurée pour permettre le téléchargement inter-origines en définissant son attribut crossOrigin à "anonymous" (c'est-à-dire, permettre le téléchargement inter-origines de l'image sans authentification). Un gestionnaire d'évènement est ajouté pour l'évènement load déclenché sur l'élément image, ce qui signifie que les données de l'image ont été reçues. Un texte alternatif est ajouté à l'image ; bien que <canvas> ne prenne pas en charge l'attribut alt, la valeur peut être utilisée pour définir un aria-label ou le contenu interne du canevas.
Enfin, l'attribut src de l'image est défini sur l'URL de l'image à télécharger ; cela déclenche le début du téléchargement.
Recevoir et enregistrer l'image
Le code qui gère l'image nouvellement téléchargée se trouve dans la méthode imageReceived() :
function imageReceived() {
let canvas = document.createElement("canvas");
let context = canvas.getContext("2d");
canvas.width = downloadedImg.width;
canvas.height = downloadedImg.height;
canvas.innerText = downloadedImg.alt;
context.drawImage(downloadedImg, 0, 0);
imageBox.appendChild(canvas);
try {
localStorage.setItem("saved-image-example", canvas.toDataURL("image/png"));
} catch (err) {
console.log(`Erreur : ${err}`);
}
}
imageReceived() est appelée pour gérer l'évènement "load" sur le HTMLImageElement qui reçoit l'image téléchargée. Cet évènement est déclenché une fois que toutes les données téléchargées sont disponibles. Elle commence par créer un nouvel élément HTML <canvas> que nous utiliserons pour convertir l'image en URL de données, et en obtenant l'accès au contexte de dessin 2D du canevas (CanvasRenderingContext2D) dans la variable context.
La taille du canevas est ajustée pour correspondre à l'image reçue, le texte interne est défini sur la description de l'image, puis l'image est dessinée dans le canevas à l'aide de drawImage(). Le canevas est ensuite inséré dans le document pour que l'image soit visible.
Il est maintenant temps d'enregistrer effectivement l'image localement. Pour cela, nous utilisons le mécanisme de stockage local de l'API Web Storage, accessible via la globale localStorage. La méthode du canevas toDataURL() est utilisée pour convertir l'image en une URL de type data:// représentant une image PNG, qui est ensuite enregistrée dans le stockage local à l'aide de setItem().