近期接到一个需求,需要圆角下载图片,为了节约后端的开销,所以决定在前端用Canvas进行图片圆角后再直接下载(老大说能在前端处理的尽量前端来-_-v),接着代码一把梭完之后,习惯性的按下F12打开开发者工具,果然不出我所料console里竟然害羞的脸红了,还抛出了如下错误:

Uncaught SecurityError: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.

定睛一看,着什么鬼?怎么这个方法还执行能失败,于是进行了一波常规操作(某度之后),发现这是由于浏览器的跨域策略限制,canvas绘制的图片不是用一个域下导致时导出就就会出发这个问题,果断按照网上的方法添加了如下代码:

let img = new Image();
img.crossOrigin = 'anonymous';
img.src = url;

嗯~~可以正常导出,舒服了,接着又来了个小需求,需要导出下载的时候自定义下文件名,我心想这个简单啊,代码又是一把梭

var a = document.createElement('a');
a.href = canvas.toDataURL("image/png");
a.download = fileName + '.png';//fileName就是自定义文件名
document.body.appendChild(a);
a.click();
a.remove();

简单测试几个图片,没啥问题,美滋滋,可以开心的去摸鱼了(^_^),正打算开启新标签摸鱼的时候,手抖不小心有点了张图片,想着点了就点了吧,再测试测试也好。没想到意外的发现谷歌浏览器下边的下载栏竟然出现了一个网络错误文件名也变成了下载,咦?这咋回事?刚刚不还好好的嘛,于是刷新页面Ctrl+F5大法,再试一次还是没啥卵用,我又去点了之前的几张图片发现居然又是正常了,唯独这张图不行,令我百思不得其解,甚至都在怀疑是不是这个图片是不是损坏了但是图片又能显示,唉好吧,又只有进行一波常规操作了,刚要打开某度,脑海灵光一闪canvas.toDataURL这个方法返回的是base64,而base64又全是字符串会不会有啥兼容性或图片过大等问题,如果我把转成Blob来下载会不会解决这个问题,果断试一试。

//base64转blob
base64toBlob(base64) {
  var arr = base64.split(','), mime = arr[0].match(/:(.*?);/)[1],
  str = atob(arr[1]), n = str.length, u8arr = new Uint8Array(n);
  while(n--) u8arr[n] = str.charCodeAt(n);
  return new Blob([u8arr], {type:mime});
}
...
var imgBase64 = canvas.toDataURL("image/png");
var blob = dataURLtoBlob(imgBase64);//base64转blob
var a = document.createElement('a');
a.href = URL.createObjectURL(blob);//从blob创建url
a.download = fileName + '.png';//fileName就是自定义文件名
document.body.appendChild(a);
a.click();
a.remove();
...

在测试测试,嗯~舒服了,二十多张测试图片都可以了,至此问题已经全部解决了,不过有点要注意下在解决跨域问题的时候image的crossOrigin属性一定要在src属性赋值之前设置为anonymous,否则这个错误你是解决不掉的!!!正确代码:

let img = new Image();
img.crossOrigin = 'anonymous';//一定要放在src的前面
img.src = url;