公司項目前段時間須要實現手機拍照上傳的功能,原本覺得用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); })