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 相关文章推荐
Javascript 面向对象编程(一) 封装
Aug 28 Javascript
JavaScript中的匀速运动和变速(缓冲)运动详细介绍
Nov 11 Javascript
Bootstarp风格的toggle效果分享
Feb 23 Javascript
easyui validatebox验证
Apr 29 Javascript
JS变量及其作用域
Mar 29 Javascript
Vue实现动态响应数据变化
Apr 28 Javascript
jQuery实现页码跳转式动态数据分页
Dec 31 jQuery
解决vue动态为数据添加新属性遇到的问题
Sep 18 Javascript
Vue中多个元素、组件的过渡及列表过渡的方法示例
Feb 13 Javascript
浅谈layer的Icon样式以及一些常用的layer窗口使用方法
Sep 11 Javascript
Vue SPA 初次进入加载动画实现代码
Nov 14 Javascript
Vue列表如何实现滚动到指定位置样式改变效果
May 09 Javascript
使用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
php empty函数判断mysql表单是否为空
2010/04/12 PHP
php加水印的代码(支持半透明透明打水印,支持png透明背景)
2013/01/17 PHP
解析php常用image图像函数集
2013/06/24 PHP
解决php接收shell返回的结果中文乱码问题
2014/01/23 PHP
php实现的漂亮分页方法
2014/04/17 PHP
ThinkPHP后台首页index使用frameset时的注意事项分析
2014/08/22 PHP
PHP中imagick函数的中文解释
2015/01/21 PHP
laravel http 自定义公共验证和响应的方法
2019/09/29 PHP
PHP dirname简单使用代码实例
2020/11/13 PHP
JS中字符问题(二进制/十进制/十六进制及ASCII码之间的转换)
2008/11/03 Javascript
js或css文件后面跟参数的原因说明
2010/01/09 Javascript
javascript在事件监听方面的兼容性小结
2010/04/07 Javascript
基于jquery的文本框与autocomplete结合使用(asp.net+json)
2012/05/30 Javascript
js触发asp.net的Button的Onclick事件应用
2013/02/02 Javascript
jquery 设置元素相对于另一个元素的top值(实例代码)
2013/11/06 Javascript
JavaScript将当前时间转换成UTC标准时间的方法
2015/04/06 Javascript
js获取本机操作系统类型的两种方法
2015/12/19 Javascript
原生javascript实现匀速运动动画效果
2016/02/26 Javascript
详解nodejs express下使用redis管理session
2017/04/24 NodeJs
vue组件父子间通信之综合练习(聊天室)
2017/11/07 Javascript
浅谈gulp创建完整的项目流程
2017/12/20 Javascript
详解vue+vuex+koa2开发环境搭建及示例开发
2018/01/22 Javascript
30分钟快速入门掌握ES6/ES2015的核心内容(上)
2018/04/18 Javascript
[07:39]第一届亚洲邀请赛回顾视频
2017/02/14 DOTA
在Python中调用ggplot的三种方法
2015/04/08 Python
Django中更新多个对象数据与删除对象的方法
2015/07/17 Python
Python 实现购物商城,含有用户入口和商家入口的示例
2017/09/15 Python
Python使用pyautogui模块实现自动化鼠标和键盘操作示例
2018/09/04 Python
分享CSS3制作卡片式图片的方法
2016/07/08 HTML / CSS
应届生高等护理求职信
2013/10/12 职场文书
如何做好总经理助理
2013/11/12 职场文书
汉语言文学专业自荐信
2014/06/11 职场文书
2015年干部教育培训工作总结
2015/05/15 职场文书
博物馆观后感
2015/06/05 职场文书
刚学完怎么用Python实现定时任务,转头就跑去撩妹!
2021/06/05 Python
日本十大血腥动漫,那些被禁播的动漫盘点
2022/03/21 日漫