Canvas 跨域图片数据获取

解决 Canvas 获取跨域图片时的污染问题

问题

在 Canvas 中使用跨域图片并尝试导出数据时,会报错:

Uncaught (in promise) DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported

这是因为 Canvas 认为跨域图片数据是”被污染的”,无法导出为 base64。

解答

为什么会被污染

当请求跨域图片时,如果未满足 CORS 条件,Canvas 会认为这些数据不可信。因为跨域图片可能包含敏感信息,直接使用可能暴露页面数据。

同域请求:Request Headers 带有 cookie,图片数据被 Canvas 信任。

跨域请求:默认情况下不符合 CORS 条件,图片数据不被 Canvas 信任。

解决方案

使用 <img> 元素的 crossOrigin 属性,该属性有两个值:

  • anonymous:CORS 请求时不发送认证信息
  • use-credentials:CORS 请求时发送认证信息(如 cookie)

代码示例

// page origin is https://a.com

const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');

const img = new Image();
img.crossOrigin = 'anonymous';
img.onload = function () {
  context.drawImage(this, 0, 0);
  context.getImageData(0, 0, img.width, img.height);
};
img.src = 'https://b.com/a.png';

必须满足的三个条件

  1. img 元素设置 crossOrigin 属性:启用 CORS 请求

  2. 服务器允许跨域:响应头设置 Access-Control-Allow-Origin

  3. 避免使用缓存:URL 加时间戳或设置 Cache-Control: no-cache

为什么要避免缓存

如果图片已通过 <img> 标签加载并缓存,后续用 JavaScript 请求时会直接返回缓存。如果缓存中的图片不是通过 CORS 请求的,或响应头中没有 Access-Control-Allow-Origin,就会导致报错。

关键点

  • Canvas 使用跨域图片必须通过 CORS 请求,否则会被标记为”污染”
  • 在 img 元素上设置 crossOrigin = 'anonymous' 启用 CORS
  • 服务器必须返回 Access-Control-Allow-Origin 响应头
  • 避免使用非 CORS 方式缓存的图片,可通过添加时间戳解决