IE11下使用canvas.toDataURL报SecurityError错误的解决方法


Posted in Javascript onNovember 19, 2017

发现问题

最近在项目中用到了 canvas 的 toDataURL 方法来获取图片的 base64 格式数据,用以上传到后台。由于之前也遇到过 canvas 被跨域图片污染不能获取数据的坑,因此这回一开始就机智的把 crossOrigin 属性值加上,代码大概如下:

const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
context.fillStyle = "black";
context.fillRect(0, 0, canvas.width, canvas.height);
const imageElement = document.createElement("img");
imageElement.crossOrigin = "Anonymous";
imageElement.onload = () => {
 context.drawImage(
 imageElement,
 params.left,
 params.top,
 canvas.width,
 canvas.height,
 0,
 0,
 canvas.width,
 canvas.height
 );

 const dataUrl = canvas.toDataURL("image/jpeg", 1);
}
imageElement.src = 'xxx';

本以为万无一失,而在 Chrome 浏览器上面也非常顺利;然而到了 IE11 上,却出现了一个莫名其妙的 SecurityError 错误:

IE11下使用canvas.toDataURL报SecurityError错误的解决方法

没有具体的报错信息,只通过提示定位到了执行 toDataURL 这一行,实在让人疑惑。

尝试

第一时间 Google 了一下,发现很多人遇到这个问题,但是并没有看到什么有效的解决办法,有些人建议使用 Fabric.js,但是看了一下觉得太麻烦了点。而在 caniuse 上,也明确标注了该方法在 IE11 上有问题。

看起来应该是 IE 上的一个 bug,于是想到了一个曲线救国的办法:获取图片 base64 数据的办法又不是只有一个,既然 toDataURL 方法支持不好,那就用别的办法:

  • 先将 canvas 转成 blob
  • 再用 FileReader 以 dataUrl 的方式读取

代码大概如下:

const reader = new FileReader();
reader.readAsDataURL(canvas.msToBlob());
reader.onloadend = () => {
 const base64data = reader.result;
};

然而这并没有什么卵用....

这回轮到了 msToBlob 方法报了 SecurityError 错误。我:???

解决

看起来可能真的是安全原因,唯一的安全原因,只可能是跨域图片了。寻思着可能在 IE 上安全策略比较严格,即使设置了 crossOrigin = "Anonymous" 还是不让读数据,于是想到了另外一个思路,既然是因为跨域,那就把跨域因素去除:

  • 使用 ajax 请求拿到图片的二进制数据
  • 将二进制数据转为 base64 格式
  • 将得到的 base64 数据作为图片元素的 src 设置并画到画布上
  • 正常调用 toDataURL

代码大致如下:

// 之前的代码
// ...
// 最后一行 imageElement.src = 'xxx' 替换:
getDataUrlBySrc('xxx').then(b64 => (imageElement.src = b64));

function getDataUrlBySrc(src: string) {
 return new Promise<string>((resolve, reject) => {
 if (Cache.localGet("isIE")) {
  const xmlHTTP = new XMLHttpRequest();
  xmlHTTP.open("GET", src, true);

  // 以 ArrayBuffer 的形式返回数据
  xmlHTTP.responseType = "arraybuffer";

  xmlHTTP.onload = function(e) {
  
  // 1. 将返回的数据存储在一个 8 位无符号整数值的类型化数组里面
  const arr = new Uint8Array(xmlHTTP.response);
  
  // 2. 转为 charCode 字符串
  const raw = Array.prototype.map
   .call(arr, charCode => String.fromCharCode(charCode))
   .join("");
   
  // 3. 将二进制字符串转为 base64 编码的字符串
  const b64 = btoa(raw);
  
  const dataURL = "data:image/jpeg;base64," + b64;
  resolve(dataURL);
  };
  xmlHTTP.onerror = function(err) {
  reject(err);
  };
  xmlHTTP.send();
 } else {
  resolve(src);
 }
 });
}

尝试了一下,成功达到了目的。

后来查阅资料得知,如果 canvas 污染了,那无论是 toDataURL 还是 toBlob,都是无法执行成功的。

缺陷

虽然这个方法可以达到目的,但是却牺牲了性能。要先请求一次图片数据不说,数据编码的转换也是相当耗时的。小图还好,如果图片比较大,例如超过 3M ,那整个流程需要花费的时间可以达到一两分钟,这是不可接受的。

canvas的toDataUrl是否会压缩图像?

使用canvas的drawImage方法将image对象draw到canvas画布上时,图片大小会显著增加,并且只能保存为PNG格式。

将canvas用toDataUrl转化为base64,即使encoderOptions设置为1,图片也会有较大幅度的减小,但是比起最初的image还是要大。如果encoderOptions使用默认的0.92,最终的图片大小和初始的是相差不多的

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
ExtJS4 组件化编程,动态加载,面向对象,Direct
May 12 Javascript
jQuery的选择器中的通配符使用介绍
Mar 20 Javascript
JavaScript判断手机号运营商是移动、联通、电信还是其他(代码简单)
Sep 25 Javascript
jQuery Easyui实现左右布局
Jan 26 Javascript
JavaScript学习笔记之ES6数组方法
Mar 25 Javascript
简单实现jQuery进度条轮播实例代码
Jun 20 Javascript
VueJS如何引入css或者less文件的一些坑
Apr 25 Javascript
JS实现延迟隐藏功能的方法(类似QQ头像鼠标放上展示信息)
Dec 28 Javascript
JavaScript对象字面量和构造函数原理与用法详解
Apr 18 Javascript
jQuery实现简单飞机大战
Jul 05 jQuery
jQuery实现二级导航菜单的示例
Sep 30 jQuery
如何实现vue的tree组件
Dec 03 Vue.js
使用DataTable插件实现异步加载数据
Nov 19 #Javascript
原生JavaScript实现Ajax异步请求
Nov 19 #Javascript
gulp安装以及打包合并的方法教程
Nov 19 #Javascript
js实现rem自动匹配计算font-size的示例
Nov 18 #Javascript
如何编写一个完整的Angular4 FormText 组件
Nov 18 #Javascript
Angular中支持SCSS的方法
Nov 18 #Javascript
VUE element-ui 写个复用Table组件的示例代码
Nov 18 #Javascript
You might like
雄兵连:第三季确定会出,不过时间未定,鹤熙是第三季的主角!
2020/03/13 国漫
初学PHP的朋友 经常问的一些问题。不断更新
2011/08/11 PHP
php 短链接算法收集与分析
2011/12/30 PHP
php 微信开发获取用户信息如何实现
2016/12/13 PHP
详谈symfony window下的安装 安装时候出现的问题以及解决方法
2017/09/28 PHP
PHP设计模式之注册树模式分析
2018/01/26 PHP
Laravel使用Queue队列的技巧汇总
2019/09/02 PHP
window.showModalDialog参数传递中含有特殊字符的处理方法
2013/06/06 Javascript
javascript学习笔记(五)原型和原型链详解
2014/10/08 Javascript
form.submit()不能提交表单的原因分析
2014/10/23 Javascript
深入理解JavaScript系列(43):设计模式之状态模式详解
2015/03/04 Javascript
聊一聊JS中this的指向问题
2016/06/17 Javascript
Vue.js第三天学习笔记(计算属性computed)
2016/12/01 Javascript
Vue.js系列之项目结构说明(2)
2017/01/03 Javascript
JS简单获得节点元素的方法示例
2018/02/10 Javascript
使用 vue-i18n 切换中英文效果
2018/05/23 Javascript
vue-router启用history模式下的开发及非根目录部署方法
2018/12/23 Javascript
谈谈JavaScript中super(props)的重要性
2019/02/12 Javascript
详释JavaScript执行环境与执行栈
2019/04/02 Javascript
解决jquery validate 验证不通过后验证正确的信息仍残留在label上的方法
2019/08/27 jQuery
vue 重塑数组之修改数组指定index的值操作
2020/08/09 Javascript
[32:36]完美世界DOTA2联赛PWL S3 LBZS vs CPG 第二场 12.12
2020/12/16 DOTA
python列表操作实例
2015/01/14 Python
解决python升级引起的pip执行错误的问题
2018/06/12 Python
Python自然语言处理 NLTK 库用法入门教程【经典】
2018/06/26 Python
浅谈PYTHON 关于文件的操作
2019/03/19 Python
详解Python函数式编程—高阶函数
2019/03/29 Python
Python Web程序搭建简单的Web服务器
2019/07/31 Python
PyCharm搭建Spark开发环境的实现步骤
2019/09/05 Python
windows 10 设定计划任务自动执行 python 脚本的方法
2019/09/11 Python
利用python、tensorflow、opencv、pyqt5实现人脸实时签到系统
2019/09/25 Python
python批量生成条形码的示例
2020/10/10 Python
建筑个人求职信范文
2014/01/25 职场文书
关于迟到的检讨书
2014/01/26 职场文书
水利局群众路线专题民主生活会发言材料
2014/09/21 职场文书
2014年乡镇纪委工作总结
2014/12/19 职场文书