公司项目前段时间须要实现手机拍照上传的功能,原本觉得用createObjectURL和canvas能够很轻松的实现,结果发现问题多多,特此记录下来。html
在IOS上,竖着拍照片时,图片预览会旋转90°,横着拍照就没问题,我实验了一下,在IOS上,只有当图片的分辨率过大会出现这种状况。git
最后实现图片预览效果借助了exif-js和megapix-image,exif-js
负责读取图片的EXIF信息,获取orientation
信息,而后用megapix-image
把图片数据渲染在img标签上,代码以下:github
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); }); }); }
思路有两种:web
用canvas的toDataURL()API,直接将base64文本传递过去ajax
本身构造File对象,ajax上传canvas
第一种方法须要服务器端作工做,并且上传数据量会增大4/3,所以此方法只做为回退方案。api
第二种方法的原理是用Uint8Array来构造Blob,再使用formData上传。
这里要注意的是:ArrayBuffer
不能被直接操做,必须经过typed array
来存取,并且Blob的构造函数也是typed array
。服务器
完整代码以下:app
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); })