TIP
最近在一个项目中需要实现一个移动端上传图片文件的需求,主要需求的是压缩并且按照比例自动裁切图片然后上传。
一听是蛮简单的,因为是在移动端使用,所以完全可以使用 HTML5 的新特性以及一些 API。
主要的思路是这样:
- 监听一个
input (type='file')
的change
事件,然后拿到文件的file
; - 把
file
转成dataURL
; - 然后用
canvas
绘制图片,绘制的时候经过算法按比例裁剪; - 然后再把
canvas
转成dataURL
; - 再把
dataURL
转成blob
; - 接着把
blob
append
到FormData
的实例对象。 - 最后上传。
主要用到的 FileReader
、canvas
、FormData
、Blob
这几个 API。
开发过程遇到了蛮多坑,特别是在android下的微信浏览器内。
监听 input(type=file) 获取文件内容。
|
对于 type
为 file
的 input
我们可以设置 accept
属性来现在我们要上传的文件类型,这里的目的是上传图片文件,所以我们可以设置:accept="image/gif, image/jpeg, image/png"
。
|
fileToDataURL: file 转成 dataURL
这里用到的是 FileReader
这个 API。
https://developer.mozilla.org/en-US/docs/Web/API/FileReader
|
compressDataURL:dataURL 图片绘制 canvas,然后经过处理(裁剪 & 压缩)再转成 dataURL
一开始是这样的
- 我们需要创建一个
Image
对象,然后把src
设置成dataURL
,获取到这张图片; - 我们需要创建一个
canvas
元素,用来处理绘制图片; - 获取裁剪的长宽比例,然后判断图片的实际长宽比例,按照最大化偏小的长或宽然后另一边采取中间部分,和 css 把 background 设置 center / cover 一个道理;
- 调用
ctx.drawImage
绘制图片; - 使用
canvas.toDataURL
把canvans
转成dataURL
。
|
一切的运行在pc端的chrome浏览器下模拟都很好,但是在移动端测试的时候发现 canvas
无法绘制出图片,发现是 img
设置 src
有延迟,导致还没获取到图片图像就开始绘制。
改进:监听 img.onload
事件来处理之后的操作:
|
dataURLtoBlob:dataURL 转成 Blob
这一步我们把 dataURL
转成 Blob
|
很完美了吗,在pc端模拟成功,在移动端chrome浏览器测试成功,但是在微信浏览器中失败,经过 try...catch
发现是在 new Blob
的时候失败。
查看之后发现是这个 API 对 Android 的支持还不明。
解决方法是利用 BlobBuilder
这个老 API 来解决:https://developer.mozilla.org/en-US/docs/Web/API/BlobBuilder
因为这个 API 已经被遗弃,不同机型和安卓版本兼容性不一致,所以需要一个判断。
解决方法:
|
把获取到的 blob append 到 FormData 实例,执行回调
这一步使用到我们之前的东西。
|
回到第一步,上传文件
|
一切似乎都很完美,pc 端模拟测试通过,但是到移动端却发现上传了一个空文件,这不科学!!!
查文档后发现这么一句话:
Note: XHR in Android 4.0 sends empty content for FormData with blob.
简直蒙蔽。
在 上找到了解决方案:http://stackoverflow.com/questions/15639070/empty-files-uploaded-in-android-native-browser/28809955#28809955
通过自己包装 FormDataShim
和重写 XMLHttpRequest.prototype.send
函数:
|
SUCCESS
重写 compressImage
|
到这一步总算成功。
参考:
http://stackoverflow.com/questions/15639070/empty-files-uploaded-in-android-native-browser/28809955#28809955
http://www.alloyteam.com/2015/04/ru-he-zai-yi-dong-web-shang-shang-chuan-wen-jian/