用canvas的toDataURL()将图片转为dataURL(base64)

假设一个应用场景:

由于某些特殊原因从服务端请求到图片路径(图片被存储在服务器上),要求通过该路径获取对应图片的 base64 dataURL。

在这个场景中,我们首先推断该图片路径是可访问的,同时还需要一种将图片转换到 dataURL 的方法。我们如何实现它呢?

dataURL

先大致回顾下正统的 dataURL 的语法,这有助于我们检验转换后的内容是否正确。一个完整的 dataURI 应该是这样的:

data:[<mediatype>][;base64],<data>

其中mediatype声明了文件类型,遵循MIME规则,如“image/png”、“text/plain”;之后是编码类型,这里我们只涉及 base64;紧接着就是文件编码后的内容了。我们常常在 HTML 里看到img标签的src会这样写:

src="data:image/gif;base64,R0lGODdhMAAwAPAAAAAAAP///ywAAAAAMAAwAAAC8IyPqcvt3wCcDkiLc7C0qwyGHhSWpjQu5yqmCYsapyuvUUlvONmOZtfzgFzByTB10QgxOR0TqBQejhRNzOfkVJ+5YiUqrXF5Y5lKh/DeuNcP5yLWGsEbtLiOSpa/TPg7JpJHxyendzWTBfX0cxOnKPjgBzi4diinWGdkF8kjdfnycQZXZeYGejmJlZeGl9i2icVqaNVailT6F5iJ90m6mvuTS4OK05M0vDk0Q4XUtwvKOzrcd3iq9uisF81M1OIcR7lEewwcLp7tuNNkM3uNna3F2JQFo97Vriy/Xl4/f1cf5VWzXyym7PHhhx4dbgYKAAA7"

这个img引用的就是以 base64 编码的 dataURL 了,只要浏览器支持,就可以被解码成声明格式的图片并渲染出来。

.toDataURL()

这是一个功能函数,FileReader对象也有类似的方法,比如.readAsDataURL(),然而它只接受fileblob类型,而这两种类型一般只能通过<input[type=file]>元素的files属性获取,或者用Blob()构造函数手工创建一个新的对象。尴尬的是我们当前只有图片路径,受制于浏览器的安全策略,<input[type=file]>files属性是只读的,而Blob()构造函数只接受文件内容,两种方式都无法通过图片路径直接获取。上文中假设的应用场景迫使我们必先考虑如何通过路径获取到图片内容。<img>是可以的,并且可以被绘制到<canvas>中,而<canvas>正巧拥有.toDataURL()方法。

万事具备,我们只需要把<img>获取到的图片放到<canvas>里再通过.toDataURL()方法转化下,就可以得到以 base64 编码的 dataURL。来看这个方法的语法:

canvas.toDataURL([type, encoderOptions]);

canvas 是 DOM 元素<canvas>对象;参数type指定图片类型,如果指定的类型不被支持则以默认值image/png替代;encoderOptions可以为image/jpegimage/webp类型的图片设置图片质量,取值0-1,超出则以默认值0.92替代(我们似乎顺带开发出了第二个功能——前端实现图片压缩 o)。

需要注意的是,图片加载是异步的,在转换成 dataURL 前必须先确保图片成功加载到,否则让 canvas 即刻执行绘制可能失败,从而导致转换 dataURL 失败。于是.toDataURL()方法应该写在<img>onload事件中,以确保 canvas 的绘制工作在图片下载完成后开始。另一个问题是<img>图片渲染到<canvas>上也需要一个过程,好在.drawImage()方法是同步的,只有在 canvas 绘制完成后才会执行后续如.toDataURL()的操作。现在就来实现一个功能函数:

function getBase64(url){
  // 通过构造函数来创建的 img 实例
  // 在赋予 src 值后就会立刻下载图片
  // 相比 createElement() 创建 <img> 省去了 append(),也就避免了文档冗余和污染
  let dataURL = ''
  let img = new Image();

  img.src = url;
  img.onload = () => { // 要先确保图片完整获取到,这是个异步处理
    let canvas = document.createElement('canvas'); // 创建canvas元素
    let [width,height] = [img.width,img.height]; // 确保canvas的尺寸和图片一样
    canvas.width = width;
    canvas.height = height;
    canvas.getContext('2d').drawImage(img, 0, 0, width, height); // 将图片绘制到canvas中
    dataURL = canvas.toDataURL('image/jpeg'); // 转换图片为dataURL
  }
}

一个可供随时调用的转换函数完成了,它会在图片被加载后返回一整个 dataURL 字符串。

完善

onload事件确保了转换任务在图片加载后执行,却又带来了新问题——dataURL 只有在图片加载完成后才会返回,我们是无法精准确定图片完成加载的时间的。如果后续要对 dataURL 做相关处理(比如传递到其他服务器)的话,添加一个回调是必要的,这能确保后续处理任务在成功得到 dataURL 之后执行,我们修改一下getBase64()

function getBase64(url, callback){ //添加一个回调参数
  ...
  img.onload = () => {
    ...
    canvas.getContext('2d').drawImage(img, 0, 0, width, height);
    dataURL=canvas.toDataURL('image/jpeg');
    callback&&callback(dataURL); //调用回调函数
  }
}

在执行时添加回调:

let imgURL = '//upload.jianshu.io/users/upload_avatars/555630/fdd1b798e6b0.jpg';

getBase64(imgURL, dataURL => {
  console.log(dataURL)
});

就是这样。如果不考虑兼容性的话,或许我们可以用 promise 和 generator 来实现,再添加一些错误处理就更完美了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
禁止转载,如需转载请通过简信或评论联系作者。

相关阅读更多精彩内容

  • 一、canvas简介 1.1 什么是canvas?(了解) 是HTML5提供的一种新标签 Canvas是一个矩形区...
    J_L_L阅读 5,485评论 0 4
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 13,897评论 1 32
  • 在项目中,需要生成海报。有动态信息(微信头像、微信昵称、上传图片(oss链接)、二维码)+ 海报背景图生成一张海报...
    奔跑吧笨笨阅读 12,346评论 0 0
  • 旋转压缩部分因为有文件的不同类型相互转换,所以方法调用比较多,看起来有点乱,但是实际不难理解,建议粘贴到编辑器比较...
    名字_d880阅读 8,550评论 0 1
  • 一、图形的组合方式 globalAlpha是一个介于0和1之间的值(包括0和1),用于指定所有绘制的透明度。默认值...
    空谷悠阅读 5,215评论 0 0

友情链接更多精彩内容