業(yè)務(wù)有個需求,要做圖片預(yù)覽上傳,過去都是客戶端上傳給后端侧但,后端返回url前端進行預(yù)覽,現(xiàn)在其實可以不依賴后端做預(yù)覽航罗,最后在上傳禀横,這主要依賴FileReader和FormData這兩個對象和JavaScript處理二進制的能力。
OK粥血,Show code~柏锄,以下代碼已注釋掉具體業(yè)務(wù)邏輯和實現(xiàn)酿箭,如果需要了解 API 細節(jié),可以請參考:
https://developer.mozilla.org/en-US/docs/Web/API/FileReader
https://developer.mozilla.org/en-US/docs/Web/API/FormData/FormData
https://developer.mozilla.org/en-US/docs/Web/API/Blob/Blob
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer
文件表單的樣式主要通過讓它后面趾娃,通過別的DOM來美化它缭嫡。
input.on.('change', preview);
預(yù)覽使用FileReader對象來讀:
functionpreview(e){varfile = e.target.files[0];varreader =newFileReader();? ? reader.onloadend =function(){// 圖片的 base64 格式, 可以直接當成 img 的 src 屬性值vardataURL = reader.result;varimg =newImage();? ? ? ? img.src = dataURL;// 插入到 DOM 中預(yù)覽// ...};? ? reader.readAsDataURL(file);// 讀出 base64}
base64 轉(zhuǎn) 二進制文件
/*** dataURL to blob, ref to https://gist.github.com/fupslot/5015897 *@paramdataURI *@returns{Blob} */function dataURItoBlob(dataURI) {? ? var byteString = atob(dataURI.split(',')[1]);? ? var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];? ? var ab =newArrayBuffer(byteString.length);? ? var ia =newUint8Array(ab);for(var i =0; i < byteString.length; i++) {? ? ? ? ia[i] = byteString.charCodeAt(i);? ? }returnnewBlob([ab], {type: mimeString});}
構(gòu)造FormData填充二進制文件數(shù)據(jù),通過ajax的方式進行提交:
varfd =newFormData();varblob = dataURItoBlob(dataURL);fd.append('file', blob);$.ajax({? ? type:'POST',? ? url:'/upload',? ? data: fd,? ? processData:false,// 不會將 data 參數(shù)序列化字符串contentType:false,// 根據(jù)表單 input 提交的數(shù)據(jù)使用其默認的 contentTypexhr:function() {varxhr =newwindow.XMLHttpRequest();? ? ? ? xhr.upload.addEventListener("progress",function(evt) {if(evt.lengthComputable) {varpercentComplete = evt.loaded / evt.total;? ? ? ? ? ? ? ? console.log('進度', percentComplete);? ? ? ? ? ? }? ? ? ? },false);returnxhr;? ? }}).success(function(res){// 拿到提交的結(jié)果}).error(function(err){console.error(err);});
注意:不要漏了指定processData和contentType為false抬闷。
業(yè)務(wù)中不需要前端不需要壓縮妇蛀,因為后端有更靠譜的壓縮方案,但是前端其實也可以壓縮笤成,那就是用canvas把圖畫出適合的大小评架,然后上傳。
在new出來的Image對象炕泳,我們監(jiān)聽它的onload事件
按照壓縮比例纵诞,算出壓縮后的圖片尺寸
創(chuàng)建canvas,尺寸設(shè)置成上一步驟算出來的壓縮后的圖片尺寸
調(diào)用drawImage方法培遵,把圖片繪制到canvas中
調(diào)用canvas的toDataURL浙芙,取出base64格式的數(shù)據(jù)
后續(xù)的傳圖步驟和上面的原圖上傳一樣
varimg =newImage();img.onload =function(){// 當圖片寬度超過 400px 時, 就壓縮成 400px, 高度按比例計算// 壓縮質(zhì)量可以根據(jù)實際情況調(diào)整varw = Math.min(400, img.width);varh = img.height * (w / img.width);varcanvas = document.createElement('canvas');varctx = canvas.getContext('2d');// 設(shè)置 canvas 的寬度和高度canvas.width = w;? ? canvas.height = h;// 把圖片繪制到 canvas 中ctx.drawImage(img,0,0, w, h);// 取出 base64 格式數(shù)據(jù)vardataURL = canvas.toDataURL('image/png');// ...};img.src = reader.result;
自己的OneWord客戶端的上傳組件就是這么做的:ow-image-uploader.vue
這樣一看,好像除去業(yè)務(wù)邏輯的話籽腕,好像也沒多少代碼
PS:需要注意的是茁裙,通過canvas繪制的圖片,低版本iOS會出現(xiàn)比例不正確的情況节仿,請參考https://github.com/stomita/ios-imagefile-megapixel
原地址:http://blog.csdn.net/huanghongfei1/article/details/61216852