H5拍照、预览、压缩、上传采坑记录
公司项目前段时间需要实现手机拍照上传的功能,本来以为用createObjectURL和canvas可以很轻松的实现,结果发现问题多多,特此记录下来。
图片预览
在IOS上,竖着拍照片时,图片预览会旋转90°,横着拍照就没问题,我实验了一下,在IOS上,只有当图片的分辨率过大会出现这种情况。
最后实现图片预览效果借助了exif-js和megapix-image,exif-js
负责读取图片的EXIF信息,获取orientation
信息,然后用megapix-image
把图片数据渲染在img标签上,代码如下:
import EXIF from '../utils/exif';
import MegaPixImage from '../utils/megapix-image';
/**
*
* @param file file对象
* @param resImg 预览IMG标签
* @returns {Promise}
*/
renderPreviewImg(file, resImg) {
return new Promise(function (resolve, reject) {
EXIF.getData(file, _=> {
var allMetaData = EXIF.getAllTags(file);
var orientation = allMetaData.Orientation;
var mpImg = new MegaPixImage(file);
mpImg.render(resImg, {
maxWidth: 1024,
maxHeight: 1024,
// quality: 0.6,
orientation: orientation
}, resolve);
});
});
}
无刷新压缩上传
思路有两种:
用canvas的toDataURL()API,直接将base64文本传递过去
自己构造File对象,ajax上传
第一种方法需要服务器端做工作,而且上传数据量会增大4/3,因此此方法只作为回退方案。
第二种方法的原理是用Uint8Array来构造Blob,再使用formData上传。
这里要注意的是:ArrayBuffer
不能被直接操作,必须通过typed array
来存取,而且Blob的构造函数也是typed array
。
完整代码如下:
this.renderPreviewImg(file, resImg)
.then(() => {
try {
var binaryData = null;
if (!Blob || !ArrayBuffer || !Uint8Array) {
// alert(123);
binaryData = file;//如果不支持压缩,直接上传原始图片
} else {
//组装二进制
var base64Data = $(resImg).attr('src');
var byteString = atob(base64Data.split(',')[1]);
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
binaryData = new Blob([ia], {
"type": file.type
});
}
this.setState({
uploadProgress: 0
});
//组装formData
var fd = new FormData();
fd.append('file', binaryData, 'img.jpg');
fd.append('token', uploadToken);
console.log(fd);
return this.uploadBinaryDataToQiniu(fd, this.uploadSuccess.bind(this), this.handleUploadProgress.bind(this))
} catch (e) {
alert(e.message);
}
}).catch(function (e) {
console.log(e);
})