å 许å¾çå canvas è·¨æºä½¿ç¨
HTML è§è为å¾çæä¾äº crossorigin 屿§ï¼ç»ååéç CORS ååºæ 头ï¼å°±å¯ä»¥å®ç°å¨ <canvas> ä¸ä½¿ç¨å¤é¨åå è½½ç <img> å
ç´ ï¼å°±åå¨å½åæºå è½½ä¸æ ·ã
æ¥ç CORS è®¾ç½®å±æ§æ¥äºè§£æ´å¤ crossorigin 屿§çç¨æ³ã
å®å ¨æ§åâ被污æâç canvas
ç±äºå¨ <canvas> ä½å¾ä¸çåç´ å¯è½æ¥èªå¤ç§æ¥æºï¼å
æ¬ä»å
¶ä»ä¸»æºæ£ç´¢çå¾åæè§é¢ï¼å æ¤ä¸å¯é¿å
çä¼åºç°å®å
¨é®é¢ã
尽管ä¸éè¿ CORS å°±å¯ä»¥å¨ canvas ä¸ä½¿ç¨å ¶ä»æ¥æºçå¾çï¼ä½æ¯è¿ä¼æ±¡æç»å¸ï¼å¹¶ä¸ä¸å认为æ¯å®å ¨çç»å¸ï¼è¿å°å¯è½å¨ canvas æ£ç´¢æ°æ®è¿ç¨ä¸å¼åå¼å¸¸ã
妿å¤é¨æºçå
å®¹æ¯ HTML <img> æ SVG <svg> å
ç´ ï¼ä¼é»æ¢ä» canvas ä¸è¯»åæ°æ®ã
妿å¤é¨æºå¾çæ¯ä½ä¸º HTMLCanvasElement æ ImageBitMap è·åçï¼ä¸å¾çæºä¸ç¬¦ååæºååï¼ä¼é»æ¢è¯»å canvas çå
容ã
å¨"被污æ"ç canvas ä¸è°ç¨ä»¥ä¸æ¹æ³å°ä¼æåºå®å ¨é误ï¼
- å¨ canvas çä¸ä¸æä¸è°ç¨
getImageData() - å¨
<canvas>å ç´ æ¬èº«è°ç¨toBlob()ãtoDataURL()æcaptureStream()
å°è¯ä»¥ä¸è¡ä¸ºä¼å¯¼è´æåº SecurityError å¼å¸¸ãè¿ç§æºå¶å¯ä»¥é¿å
æªç»è®¸å¯æåè¿ç¨ç½ç«ä¿¡æ¯è导è´çç¨æ·éç§æ³é²ã
åå¨ä¸å¼ å¤é¨æ¥æºä¸çå¾ç
å¨ä»¥ä¸ä¾åä¸ï¼å°å®ç°å 许æ£ç´¢æ¥èªå¤é¨çå¾åï¼å¹¶å°å ¶ä¿å卿¬å°åå¨ãè¦å®ç°è¿ä¸ç¹ï¼æä»¬éè¦é ç½®æå¡å¨ä»¥åç½ç«æ¬èº«çç¼ç ã
ç½ç«æå¡å¨é ç½®
é¦å
ï¼ä½ å¿
é¡»æä¸ä¸ªå¯ä»¥å¯¹å¾çæ£ç¡®ååº Access-Control-Allow-Origin æ 头ï¼ä»¥å
许跨æºè®¿é®çæå¡å¨ã
æä»¬å设æå¡å¨æ£å¨ä½¿ç¨ Apacheãä½ å¯ä»¥åèä»¥ä¸ HTML5 æ ·æ¿ä¸ºè·¨æºå¾çåå¤ç 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://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;
}
å¨è¿éï¼æä»¬ä½¿ç¨äºâ硬ç¼ç âçå¾çç½åï¼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}`);
}
}
å¨ HTMLImageElement è°ç¨ imageReceived() æ¥å¤ç "load" äºä»¶ä»¥æ¥æ¶ä¸è½½å¾çã䏿¦ä¸è½½å®æå
¨é¨ææçæ°æ®å³å¯è§¦å该äºä»¶ãé¦å
æä»¬ä¼å建ç¨äºè½¬æ¢å¾çå°æ°æ® URL ç <canvas> å
ç´ ï¼ç¶åå¨åé context ä¸è·å canvas ç 2D ç»ç»ä¸ä¸æï¼CanvasRenderingContext2Dï¼ã
è°æ´ç»å¸å¤§å°ä»¥å¹é
æ¥æ¶å°çå¾åï¼å
鍿åå°è®¾ç½®ä¸ºå¾ççæè¿°ï¼ç¶åä½¿ç¨ drawImage() å°å¾åç»å¶å°ç»å¸ä¸ãç»å¸ä¹å被æå
¥å°ææ¡£ä¸æ¾ç¤ºã
ç°å¨æ¯æ¶åæ¬å°åå¨å¾çäºãæä»¬ä½¿ç¨ Web Storage API çæ¬å°å卿ºå¶æ¥å°å¾çå®é
åå¨å°æ¬å°ï¼è¯¥æºå¶å¯éè¿å
¨å± localStorage è¿è¡è®¿é®ãcanvas ç toDataURL() æ¹æ³ç¨äºå°å¾å转æ¢ä¸º data:// URL å½¢å¼ç PNG æ ¼å¼å¾çï¼ä¹åä½¿ç¨ setItem() åå¨å°æ¬å°ã