Verwendung von Cross-Origin-Bildern in einem Canvas

HTML bietet ein crossorigin Attribut für Bilder, das in Kombination mit einem entsprechenden CORS Header Bilder vom <img> Element erlaubt, die von fremden Ursprüngen geladen werden, in einem <canvas> verwendet zu werden, als wären sie vom aktuellen Ursprung geladen worden.

Siehe CORS-Einstellungen Attribute für Details, wie das crossorigin Attribut verwendet wird.

Sicherheit und verunreinigte Canvas-Elemente

Da die Pixel eines Canvas-Bitmaps aus verschiedenen Quellen stammen können, einschließlich Bildern oder Videos von anderen Hosts, ist es unvermeidlich, dass Sicherheitsprobleme auftreten können.

Sobald Sie ein Canvas mit Daten füllen, die von einem anderen Ursprung ohne CORS-Zulassung geladen wurden, wird das Canvas verunreinigt. Ein verunreinigtes Canvas gilt nicht mehr als sicher, und jeder Versuch, Bilddaten vom Canvas abzurufen, führt dazu, dass eine Ausnahme ausgelöst wird.

Wenn die Quelle des fremden Inhalts ein HTML <img> oder SVG <svg> Element ist, ist es nicht erlaubt, den Inhalt des Canvas abzurufen.

Wenn der fremde Inhalt von einem Bild stammt, das entweder als HTMLCanvasElement oder ImageBitMap bezogen wurde, und die Bildquelle die gleichen Ursprungsregeln nicht erfüllt, werden Versuche, den Inhalt des Canvas zu lesen, blockiert.

Das Aufrufen einer der folgenden Methoden auf einem verunreinigten Canvas führt zu einem Fehler:

Der Versuch, eine dieser Aktionen auszuführen, wenn das Canvas verunreinigt ist, führt dazu, dass ein SecurityError ausgelöst wird. Dies schützt die Benutzer davor, dass private Daten durch das Verwenden 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, dass Bilder von einem fremden Ursprung abgerufen und im lokalen Speicher gespeichert werden. Die Implementierung erfordert sowohl die Konfiguration des Servers als auch das Schreiben von Code für die Website selbst.

Webserver-Konfiguration

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

Nehmen wir an, wir betreiben unsere Website mit Apache. Betrachten Sie die HTML5 Boilerplate Apache-Server-Konfigurationsdatei für CORS-Bilder, 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 (die mit den Erweiterungen ".bmp", ".cur", ".gif", ".ico", ".jpg", ".jpeg", ".png", ".svg", ".svgz" und ".webp") von überall im Internet Cross-Origin zugegriffen werden können.

Implementierung der Speicherfunktion

Jetzt, da der Server so konfiguriert ist, dass er die Bilder Cross-Origin abrufen lässt, können wir den Code schreiben, der dem Benutzer erlaubt, sie im lokalen Speicher zu speichern, als ob sie von derselben Domain serviert würden, auf der der Code ausgeführt wird.

Der Schlüssel liegt darin, das crossorigin Attribut zu verwenden, indem crossOrigin am HTMLImageElement gesetzt wird, in dem das Bild geladen wird. Dies teilt dem Browser mit, beim Herunterladen der Bilddaten Cross-Origin-Zugriff zu verlangen.

Starten des Downloads

Der Code, der den Download startet (zum Beispiel, wenn der Benutzer auf einen "Download"-Button 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 einen zugehörigen beschreibenden Text (imageDescription), aber das könnte leicht von irgendwoher kommen. Um den Download des Bildes zu starten, erstellen wir ein neues HTMLImageElement Objekt mit dem Image() Konstruktor. Das Bild wird dann so konfiguriert, dass Cross-Origin-Downloads zugelassen werden, indem das crossOrigin Attribut auf "anonymous" gesetzt wird (das bedeutet, dass das Bild Cross-Origin ohne Authentifizierung heruntergeladen werden darf). Ein Event-Listener wird für das load Ereignis, das auf dem Bildelement ausgelöst wird, hinzugefügt, was bedeutet, dass die Bilddaten empfangen wurden. Alternativer Text wird dem Bild hinzugefügt; während <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 Start des Downloads aus.

Empfangen und Speichern des Bildes

Der Code, der das neu heruntergeladene Bild verarbeitet, 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 zu behandeln, das das heruntergeladene Bild empfängt. Dieses Ereignis wird ausgelöst, sobald die heruntergeladenen Daten vollständig verfügbar sind. Es beginnt, indem ein neues <canvas> Element erstellt wird, das wir verwenden, um das Bild in eine Daten-URL zu konvertieren, und indem der Zugriff auf den 2D-Zeichnungskontext des Canvas (CanvasRenderingContext2D) in der Variablen context erfolgt.

Die Größe des Canvas wird angepasst, um dem empfangenen Bild zu entsprechen, 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, damit das Bild sichtbar ist.

Jetzt ist es an der Zeit, das Bild tatsächlich lokal zu speichern. Dazu verwenden wir den lokalen Speichermekanismus 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, die ein PNG-Bild repräsentiert, zu konvertieren, das dann unter Verwendung von setItem() im lokalen Speicher gespeichert wird.

Siehe auch