尽管不通过 CORS 就可以在 Canvas 画布中使用图片,但是这会污染画布。一旦画布被污染,你就无法读取其数据。例如,你不能再使用画布的 toBlob(), toDataURL() 或 getImageData() 方法,调用它们会抛出安全错误。这种机制可以避免未经许可拉取远程网站信息而导致的用户隐私泄露。
HTML 规范中图片有一个 crossorigin 属性,结合合适的 CORS 响应头,就可以实现在画布中使用跨域 元素的图像。
crossOrigin/CORS | 同域 | 跨域无 CORS | 跨域有 CORS |
---|---|---|---|
default | 支持 | 支持渲染,不支持 toDataURL |
支持渲染,不支持 toDataURL |
anonymous | N/A | 同上 | 支持渲染,支持 toDataURL |
use-credentials | N/A | 同上 | 支持渲染,不支持 toDataURL |
总结:Canvas 可以正常的渲染跨域图片,但是在跨域图片没有设置跨域响应头或没有设置 crossOrigin = 'anonymous' 的时候,使用 canvas.toDataURl 会抛出如下错误:
Chrome
没有设置 crossOrigin
Uncaught DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.
at Image.img.onload...
跨域
Access to Image at 'http://localhost:3001/canvas.jpg' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access.
设置了 crossOrigin=use-credentials
Access to Image at 'http://localhost:3002/canvas.jpg' from origin 'http://localhost:3000' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. Origin 'http://localhost:3000' is therefore not allowed access.
Safari/Firefox
没有设置 crossOrigin
SecurityError: The operation is insecure.
跨域
[Error] Origin http://192.168.3.99:3000 is not allowed by Access-Control-Allow-Origin.
[Error] Failed to load resource: Origin http://192.168.3.99:3000 is not allowed by Access-Control-Allow-Origin. (canvas.jpg, line 0)
[Error] Cross-origin image load denied by Cross-Origin Resource Sharing policy.
设置了 corssOrigin=use-credentials
[Error] Cannot use wildcard in Access-Control-Allow-Origin when credentials flag is true.
[Error] Failed to load resource: Cannot use wildcard in Access-Control-Allow-Origin when credentials flag is true. (canvas.jpg, line 0)
[Error] Cross-origin image load denied by Cross-Origin Resource Sharing policy.
解决方案crossOrigin:
给img标签加上'crossOrigin'属性即可解决图片跨域问题
// 加载图片
loadImage(url, isCalc) {
return new Promise((resolve, reject) => {
const img = new Image();
img.crossOrigin = "anonymous";
img.onload = () => {
console.log('图片测试url:',img);
resolve(img);
};
img.onerror = () => resolve("");
img.src = url;
let that = this;
});
},
解决方案XMLHttpRequest:
通过 XMLHttpRequest 请求img,需要后台 header("Access-Control-Allow-Origin: *");
// 加载图片
loadImage(url, isCalc) {
return new Promise((resolve, reject) => {
url =
"https://mspace-static.gmmc.com.cn/upload/prod/image/avatar/2021-04-23/00e6dc2f-2eed-4209-97f9-55326134c4a6.jpg";
let img = new Image();
let xhr = new XMLHttpRequest();
xhr.open("get", url, true);
xhr.responseType = "blob";
xhr.onload = function () {
if (this.status == 200) {
img.src = window.URL.createObjectURL(this.response);
img.onload = () => {
console.log(img.width, img.height);
resolve(img);
};
}
};
xhr.send();
});
}