html2canvas-绘制CORS图片污染画布引起的问题与疑惑

bolelee
  • 1.2k

首先说明一下,使用跨域图片绘制会污染canvas画布,导致无法导出canvas数据,转化成图片,这是canvas的特点,不关html2canvas的事,只是此处需求是使用这个库将html转化为图片,故在标题中标上了html2canvas, 标题提得不恰当,有更合适的可以指出,请不要上来就喷

背景与需求
将某段html转化为图片并可下载到本地。

使用方案
通过html2canvas先将html转化为canvas,再通过canvas.toDataURL将canvas转化为图片

遇到的问题
绘图元素中使用的图片存放在阿里云,因此存在非同源CORS问题,图片可以绘制(allowTaint: true), 但这样的话,画布被污染,就无法导出canvas的数据转化为图片了

尝试过的方案
方案:foreignObjectRendering: true
结果:图片无法显示,绘图错乱

方案:组合使用allowTaint, useCORS, foreignObjectRendering, onclone
结果:未达效果

方案:图片存放的服务器设置允许跨域(后端同事很确定已经设置了允许跨域访问),转化canvas时设置(allowTaint: false, useCORS: true)
结果:不知何故,仍提示图片跨域,不允许访问 Access to image at 'xxx' from origin 'xxx' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

方案:将图片转化为base64格式后,再进行绘图,这样就不会污染画布了
方法:通过xhr请求,设置其responseType为'blob',获得数据后,通过FileReaderreadAsDataURL(blob)转化为base64格式
问题:xhr请求数据时,就被CORS挡在门外了

方案:设置image crossorigin属性
结果:未达效果

相关的方案链接:
convert all images to base64 after onclone

官方方案html2canvas: proxy

官方指导-proxy
关于Proxy使用的issue
官方指导的nodejs示例

疑惑
我看了官方指导的nodejs示例源码及使用示例,粗略理解为:它将图片转化为base64格式,结合使用设置(proxy: theProxyURL), 绘制到跨域图片时,会去访问theProxyURL下转化好格式的图片,由此解决了画布污染问题。

我感到有些疑惑,这样的转化方式,就不会遇到CORS问题了吗?使用proxy可以解决这个问题的关键究竟在哪里?

我的这个理解是否正确?若用此方式,是否单独部署proxy相关代码,将proxy设置为其地址即可?

回复
阅读 7.5k
2 个回答

mark:
由于canvas画布污染的原因是图片跨域,使用base64格式的图片,即可解决问题,因此,此处未尝试proxy方案(要单独部署),而采取了其他方案:让后端返回base64格式的图片。

相关后续:
需求:用户可主动下载html转化的图片
方法:将base64转化为blob, 再通过createObjectURLblob`转化为url。设置a标签`hrefdownload`属性,达到点击该a标签将图片下载到本地的目的。
附主要方法:

function base64ToBlob(base64) {
  let parts = base64.split(';base64,');
  let contentType = parts[0].split(':')[1];
  let raw = window.atob(parts[1]);
  let rawLength = raw.length;

  let uInt8Array = new Uint8Array(rawLength);

  for (let i = 0; i < rawLength; ++i) {
    uInt8Array[i] = raw.charCodeAt(i);
  }
  return new Blob([uInt8Array], {type: contentType});
}

function getObjectURL(file) {
  var url = null;
  if (window.createObjectURL !== undefined) { // basic
    url = window.createObjectURL(file);
  } else if (window.URL !== undefined) { // mozilla(firefox)
    url = window.URL.createObjectURL(file);
  } else if (window.webkitURL !== undefined) { // webkit or chrome
    url = window.webkitURL.createObjectURL(file);
  }
  return url;
}
宣传栏