在 Canvas 中使用跨域圖片

HTML 為圖片提供了 crossorigin 屬性,結合適當的 CORS 標頭,它允許從外部源載入的 <img> 元素定義的圖片在 <canvas> 中使用,就像它們是從當前源載入的一樣。

有關 crossorigin 屬性如何使用的詳細資訊,請參閱 CORS 設定屬性

安全性和被“汙染”的 Canvas

由於 Canvas 點陣圖中的畫素可能來自各種來源,包括從其他主機檢索的圖片或影片,因此不可避免地會出現安全問題。

一旦你在 Canvas 中繪製了任何未經 CORS 批准從其他源載入的資料,Canvas 就會被汙染。一個被汙染的 Canvas 不再被認為是安全的,任何嘗試從 Canvas 中檢索圖片資料的行為都會導致丟擲異常。

如果外部內容源是 HTML <img> 或 SVG <svg> 元素,則不允許嘗試檢索 Canvas 的內容。

如果外部內容來自 HTMLCanvasElementImageBitMap 獲取的圖片,並且圖片源不符合同源策略,則嘗試讀取 Canvas 內容將被阻止。

對被汙染的 Canvas 呼叫以下任何方法都會導致錯誤

當 Canvas 被汙染時嘗試執行這些操作將導致丟擲 SecurityError。這可以保護使用者,防止透過使用圖片從遠端網站未經許可地獲取資訊,從而暴露私人資料。

儲存來自外部源的圖片

在此示例中,我們希望允許從外部源檢索圖片並將其儲存到本地儲存。實現這一點需要配置伺服器併為網站本身編寫程式碼。

Web 伺服器配置

我們首先需要一個配置為託管圖片的伺服器,其中 Access-Control-Allow-Origin 標頭配置為允許跨域訪問圖片檔案。

假設我們正在使用 Apache 提供我們的網站。請考慮 HTML5 Boilerplate 的 用於 CORS 圖片的 Apache 伺服器配置檔案,如下所示

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

簡而言之,這會將伺服器配置為允許圖形檔案(副檔名為“.bmp”、“.cur”、“.gif”、“.ico”、“.jpg”、“.jpeg”、“.png”、“.svg”、“ .svgz" 和 ".webp")可以從網際網路上的任何位置進行跨域訪問。

實現儲存功能

現在伺服器已配置為允許跨域檢索圖片,我們可以編寫程式碼,允許使用者將它們儲存到 本地儲存,就像它們是從執行程式碼的同一域提供的一樣。

關鍵是透過將 HTMLImageElement 上的 crossOrigin 設定為 crossorigin 屬性來允許圖片載入。這會告訴瀏覽器在下載圖片資料時請求跨域訪問。

開始下載

開始下載的程式碼(例如,當用戶點選“下載”按鈕時)如下所示

js
function startDownload() {
  let imageURL = "https://mdn.github.io/shared-assets/images/examples/mdn.svg";
  let imageDescription = "Logo of a dinosaur in front of a map";

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

我們在這裡使用硬編碼的 URL (imageURL) 和相關的描述性文字 (imageDescription),但這可以很容易地來自任何地方。要開始下載圖片,我們使用 Image() 建構函式建立一個新的 HTMLImageElement 物件。然後將圖片配置為透過將其 crossOrigin 屬性設定為 "anonymous" 來允許跨域下載(即,允許未經身份驗證的跨域下載圖片)。為圖片元素上觸發的 load 事件添加了一個事件監聽器,這意味著圖片資料已收到。為圖片添加了替代文字;雖然 <canvas> 不支援 alt 屬性,但該值可用於設定 aria-label 或 Canvas 的內部內容。

最後,圖片的 src 屬性設定為要下載圖片的 URL;這會觸發下載開始。

接收並儲存圖片

處理新下載圖片的程式碼在 imageReceived() 方法中

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() 被呼叫來處理接收下載圖片的 HTMLImageElement 上的 "load" 事件。一旦所有下載資料都可用,此事件就會觸發。它首先建立一個新的 <canvas> 元素,我們將使用它將圖片轉換為資料 URL,並透過變數 context 獲取 Canvas 的 2D 繪圖上下文 (CanvasRenderingContext2D) 的訪問許可權。

Canvas 的大小調整為與接收到的圖片匹配,內部文字設定為圖片描述,然後使用 drawImage() 將圖片繪製到 Canvas 中。然後將 Canvas 插入文件中,以便圖片可見。

現在是時候實際在本地儲存圖片了。為此,我們使用 Web Storage API 的本地儲存機制,該機制透過 localStorage 全域性訪問。Canvas 方法 toDataURL() 用於將圖片轉換為表示 PNG 圖片的 data:// URL,然後使用 setItem() 將其儲存到本地儲存中。

另見