在 Canvas 中使用跨域圖片
HTML 為圖片提供了 crossorigin 屬性,結合適當的 CORS 標頭,它允許從外部源載入的 <img> 元素定義的圖片在 <canvas> 中使用,就像它們是從當前源載入的一樣。
有關 crossorigin 屬性如何使用的詳細資訊,請參閱 CORS 設定屬性。
安全性和被“汙染”的 Canvas
由於 Canvas 點陣圖中的畫素可能來自各種來源,包括從其他主機檢索的圖片或影片,因此不可避免地會出現安全問題。
一旦你在 Canvas 中繪製了任何未經 CORS 批准從其他源載入的資料,Canvas 就會被汙染。一個被汙染的 Canvas 不再被認為是安全的,任何嘗試從 Canvas 中檢索圖片資料的行為都會導致丟擲異常。
如果外部內容源是 HTML <img> 或 SVG <svg> 元素,則不允許嘗試檢索 Canvas 的內容。
如果外部內容來自 HTMLCanvasElement 或 ImageBitMap 獲取的圖片,並且圖片源不符合同源策略,則嘗試讀取 Canvas 內容將被阻止。
對被汙染的 Canvas 呼叫以下任何方法都會導致錯誤
- 對 Canvas 的上下文呼叫
getImageData() - 對
<canvas>元素本身呼叫toBlob()、toDataURL()或captureStream()
當 Canvas 被汙染時嘗試執行這些操作將導致丟擲 SecurityError。這可以保護使用者,防止透過使用圖片從遠端網站未經許可地獲取資訊,從而暴露私人資料。
儲存來自外部源的圖片
在此示例中,我們希望允許從外部源檢索圖片並將其儲存到本地儲存。實現這一點需要配置伺服器併為網站本身編寫程式碼。
Web 伺服器配置
我們首先需要一個配置為託管圖片的伺服器,其中 Access-Control-Allow-Origin 標頭配置為允許跨域訪問圖片檔案。
假設我們正在使用 Apache 提供我們的網站。請考慮 HTML5 Boilerplate 的 用於 CORS 圖片的 Apache 伺服器配置檔案,如下所示
<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 屬性來允許圖片載入。這會告訴瀏覽器在下載圖片資料時請求跨域訪問。
開始下載
開始下載的程式碼(例如,當用戶點選“下載”按鈕時)如下所示
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() 方法中
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() 將其儲存到本地儲存中。