前端上传图片到服务器,通常会有两种方式:前端
1. Base64编码nginx
2. FormData文件流canvas
首先,若是图片转为 Base64 后,除了体积会增大33%左右外,更大的问题是,你觉得你是在以字符串的格式传输二进制。 其实非也,尤为是你把它转为 JSON 传给后端,因为变成了须要解析的字符串,对后端的压力会陡增,你会明显看处处理速度的急剧下滑。以及,你还须要去调 nginx 配置文件以免 nginx 以为你字符串过长,直接报错 Entity Too Large
把包拒掉。后端
因此,Base64 更适合用来处理小图,好比头像或小图标之类,保存在本地,以减小 Http 的请求次数;而大图比较适合用 FormData 文件流来传输。服务器
而我最近的需求是把手机拍照的图片上传到服务器,因此肯定了用文件流来处理。app
而咱们的应用是通过 Hbuilder 打包后的 Web App, 加持了 iOS 和 Android 的原生 SDK 的功能,因此面临着两个选择:函数
<input>
来拍照及选取相册若是用原生 <input>
,能够这么写(项目用的是 Vue):ui
<input type="file" accept="image/*" capture="camera" @change="onChange" multiple>
编码
实际上呢,坑还很多。。。code
加上capture="camera"
这个属性,是为了让手机可选相机,不过真实状况是:
部分 Android 手机上调用不了相机,始终只能选相册;(解决办法:请教 Android 原生开发的童鞋吧)
苹果手机上,只能拍照,不能选相册;(解决办法:判断 iOS,若是是 iOS 就去掉 capture 属性)
因此, Android 不能调用相机这点,就把 Web 原生的方案毙掉了,后来转用 HBuilder 的 SDK;
接下来的大体思路:
获取到 File 对象
FileReader 转成 dataUrl
canvas 接收 dataUrl
对图片进行宽高比的设置(压缩大小)
再canvas.toDataURL
将压缩后的图片生成新的 Base64 编码
执行回调函数,将新的 Base64 转换为文件
最后拿到文件,构造 FormData 对象上传至服务器。
特别要注意的是:
FormData 的请求方式,每一个参数都须要经过 append 方法添加进去;
不须要单独设置请求头的 Content-Type
,系统自动会设定为 multipart/form-data
通过几天线上的验证,事实证实,只用文件流传图片,有时也会报 Entity too large
这个错误,后来通过定位发如今 canvas.toDataURL
时,最好用 jpeg 格式进行压缩,不要用 png 的无损压缩,这点很是关键!
---The End