Anleitung zur Verwendung von <img> mit Cross-Origin-Bildern und <canvas>

HTML stellt ein crossorigin Attribut für Bilder bereit, das in Kombination mit einem entsprechenden CORS-Header es ermöglicht, Bilder, die durch das <img> Element definiert sind und von fremden Ursprüngen geladen werden, in einem <canvas> zu verwenden, als wären sie vom aktuellen Ursprung geladen.

Sehen Sie sich CORS-Einstellungsattribute an, um zu erfahren, wie das crossorigin-Attribut verwendet wird.

Sicherheit und kontaminierte Canvas-Elemente

Da die Pixel in einem Bitmap eines Canvas aus verschiedenen Quellen stammen können, einschließlich Bilder oder Videos, die von anderen Hosts abgerufen werden, ist es unvermeidlich, dass Sicherheitsprobleme auftreten können.

Sobald Sie in ein Canvas Daten einfügen, die ohne CORS-Zulassung von einem anderen Ursprung geladen wurden, wird das Canvas kontaminiert. Ein kontaminiertes Canvas wird als nicht mehr sicher angesehen, und jeder Versuch, Bilddaten zurück vom Canvas abzurufen, führt zu einer Exception.

Wenn die Quelle des fremden Inhalts ein HTML <img> oder SVG <svg> Element ist, ist der Versuch, die Inhalte des Canvas abzurufen, nicht erlaubt.

Wenn der fremde Inhalt aus einem Bild stammt, das entweder als HTMLCanvasElement oder ImageBitMap erhalten wurde und die Bildquelle nicht die Regeln desselben Ursprungs erfüllt, werden Versuche, die Inhalte des Canvas zu lesen, blockiert.

Der Aufruf der folgenden Methoden auf einem kontaminierten Canvas führt zu einem Fehler:

Der Versuch, eines dieser Dinge zu tun, wenn das Canvas kontaminiert ist, wird dazu führen, dass ein SecurityError ausgelöst wird. Dies schützt Benutzer davor, dass private Daten durch die Verwendung von Bildern zum Abrufen von Informationen von entfernten Websites ohne Erlaubnis offengelegt werden.

Speichern eines Bildes von einem fremden Ursprung

In diesem Beispiel möchten wir erlauben, Bilder von einem fremden Ursprung abzurufen und im lokalen Speicher zu speichern. Die Implementierung erfordert die Konfiguration des Servers sowie das Schreiben von Code für die Website selbst.

Konfiguration des Webservers

Das Erste, was wir benötigen, ist ein Server, der so konfiguriert ist, dass er Bilder mit dem konfigurierten Access-Control-Allow-Origin Header hostet, um den Cross-Origin-Zugriff auf Bilddateien zu erlauben.

Nehmen wir an, wir stellen unsere Seite mit Apache bereit. Betrachten Sie die Apache-Serverkonfigurationsdatei für CORS-Bilder von HTML5 Boilerplate, wie unten gezeigt:

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

Kurz gesagt, dies konfiguriert den Server so, dass Grafikdateien (jene mit den Erweiterungen ".bmp", ".cur", ".gif", ".ico", ".jpg", ".jpeg", ".png", ".svg", ".svgz" und ".webp") von überall im Internet Cross-Origin abgerufen werden können.

Implementierung der Speicherfunktion

Nachdem der Server so konfiguriert wurde, dass er den Abruf der Bilder Cross-Origin erlaubt, können wir den Code schreiben, der es dem Benutzer erlaubt, sie im lokalen Speicher zu speichern, als ob sie vom gleichen Domain-Standort bereitgestellt würden, auf dem der Code läuft.

Der Schlüssel ist die Verwendung des crossorigin Attributs, indem crossOrigin auf dem HTMLImageElement gesetzt wird, in das das Bild geladen werden soll. Dies teilt dem Browser mit, beim Herunterladen der Bilddaten Zugriff über Cross-Origin anzufordern.

Start des Downloads

Der Code, der den Download startet (zum Beispiel, wenn der Benutzer auf eine "Download"-Schaltfläche klickt), sieht so aus:

js
function startDownload() {
  let imageURL =
    "https://cdn.glitch.com/4c9ebeb9-8b9a-4adc-ad0a-238d9ae00bb5%2Fmdn_logo-only_color.svg?1535749917189";
  let imageDescription = "The Mozilla logo";

  downloadedImg = new Image();
  downloadedImg.crossOrigin = "anonymous";
  downloadedImg.addEventListener("load", imageReceived, false);
  downloadedImg.alt = imageDescription;
  downloadedImg.src = imageURL;
}

Wir verwenden hier eine fest codierte URL (imageURL) und zugehörigen beschreibenden Text (imageDescription), aber das könnte leicht von überall herkommen. Um mit dem Herunterladen des Bildes zu beginnen, erstellen wir ein neues HTMLImageElement Objekt mit dem Image() Konstruktor. Das Bild wird dann so konfiguriert, dass es das Herunterladen über Cross-Origin durch Setzen seines crossOrigin Attributs auf "anonymous" (d. h. erlaubt nicht authentifiziertes Herunterladen des Bildes über Cross-Origin) erlaubt. Ein Event-Listener wird für das load Ereignis hinzugefügt, das auf dem Bild-Element ausgelöst wird, was bedeutet, dass die Bilddaten empfangen wurden. Alternativer Text wird dem Bild hinzugefügt; da <canvas> das alt Attribut nicht unterstützt, kann der Wert verwendet werden, um ein aria-label oder den inneren Inhalt des Canvas zu setzen.

Schließlich wird das src Attribut des Bildes auf die URL des herunterzuladenden Bildes gesetzt; dies löst den Beginn des Downloads aus.

Empfang und Speicherung des Bildes

Der Code, der das neu heruntergeladene Bild behandelt, befindet sich in der imageReceived() Methode:

js
function imageReceived() {
  const canvas = document.createElement("canvas");
  const 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.error(`Error: ${err}`);
  }
}

imageReceived() wird aufgerufen, um das "load" Ereignis auf dem HTMLImageElement, das das heruntergeladene Bild erhält, zu behandeln. Dieses Ereignis wird ausgelöst, sobald die heruntergeladenen Daten verfügbar sind. Es beginnt mit der Erstellung eines neuen <canvas> Elements, das wir verwenden, um das Bild in eine Daten-URL zu konvertieren, und indem wir Zugriff auf den 2D-Zeichenkontext des Canvas (CanvasRenderingContext2D) in der Variablen context erhalten.

Die Größe des Canvas wird an das empfangene Bild angepasst, der innere Text wird auf die Bildbeschreibung gesetzt, dann wird das Bild mit drawImage() in das Canvas gezeichnet. Das Canvas wird dann in das Dokument eingefügt, sodass das Bild sichtbar ist.

Jetzt ist es an der Zeit, das Bild tatsächlich lokal zu speichern. Um dies zu tun, verwenden wir den lokalen Speichermechanismus der Web Storage API, auf den über das globale localStorage zugegriffen wird. Die Canvas-Methode toDataURL() wird verwendet, um das Bild in eine data:// URL zu konvertieren, die ein PNG-Bild darstellt, das dann im lokalen Speicher mithilfe von setItem() gespeichert wird.

Siehe auch