canvas中绘制的资源跨域解决方法和原因

374 阅读1分钟

背景

在工作中,绘制了一个canvas,并且画了一些图片在canvas上

在转为图片base64的时候报错了:

使用toDataURL,提示:Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.

原因定位

换了几个静态视频源,发现都有问题,我们做了几个测试进行排查:

  1. 会不会没配置CORS,查明这些资源在服务端已经配置了CORS跨域,也依然不行
  2. 是不是公司的亚马逊S3不行,更换了公司其他云存储服务商资源节点的也依然不行
  3. 资源标签设置了跨域属性也依然不行
  4. 在这个页面中,将视频挂载在其他除canvas外的dom上却没问题,能够正常导出不报错
  5. 从项目本地的资源却没有问题,能够正常导出不报错

而后推断出: canvas的同源策略跨域更严格,仅仅配置了CORS响应头还不够,还需要做到资源URL与当前域名一致,协议和域名和端口一致

解决方法

将你的图片资源转为blob地址后放入video或者img,而后绘入canvas即可

转化办法

/**
   * 将指定 URL 的资源下载为 Blob 对象的地址
   * @param {string} url - 要下载的资源的 URL
   * @returns {Promise<string>} 包含 Blob 地址的 Promise 对象
*/
export const urlDownload2Blob = (url) => {
  return new Promise((resolve, reject) => {
    fetch(url)
      .then(res => {
        if (res.status !== 200) {
          reject() // 如果响应状态不是200,则拒绝 Promise
        }
        return res
      })
      .then(res => res.blob()) // 将响应数据转换为 Blob 对象
      .then(blob => {
        // 兼容 Safari
        window.URL = window.URL || window.webkitURL
        // 将 Blob 对象转换为 Blob 地址
        const blobUrl = window.URL.createObjectURL(blob)
        resolve(blobUrl) // 解析 Promise 并返回 Blob 地址
      })
  })
}