前端圖片壓縮及上傳
圖片的上傳一般情況下不需要上傳大體積的圖片,因為如果是使用者頭像或者是一些要求清晰度不是太高的場景上傳大體積圖片會很消耗資源,一個是上傳耗時比較長,同時也增加了儲存的開銷,當展示的時候也會消耗下載的頻寬,影響載入效率。要求使用者上傳的圖片之前壓縮圖片很影響使用者體驗,所以就增加了在前端進行圖片壓縮的需求。
壓縮方案
前端圖片壓縮的主要思路就是將圖片繪製到canvas中,然後通過canvas的toDataURL方法來控制圖片的質量,對圖片進行壓縮,另一方面是對圖片進行寬高等比縮小來達到圖片壓縮的效果,下面來看一下程式碼示例:
resizeMe(img,type, max_width, max_height) { var canvas = document.createElement('canvas'); var width = img.width; var height = img.height; max_width = !isNaN(max_width)?max_width:0; max_height = !isNaN(max_height)?max_height:0; // 在這裡圖片是等比例縮放的,呼叫方法時填入圖片允許的最大寬度或者是最大的高度 //如果最大寬度為0 則按照最大高度固定,寬度自適應的方式來實現 //如果是最大高度為0,則按照最大的寬度來實現 if(max_width==0){ if (height > max_height) { width = Math.round(width *= max_height / height); height = max_height; } } if(max_height==0){ if (width > max_width) { height = Math.round(height *= max_width / width); width = max_width; } } canvas.width =width; canvas.height = height; var ctx = canvas.getContext("2d"); canvas.width =width; canvas.height = height; ctx.drawImage(img,0,0, width, height); type = type === 'jpg'?"jpeg":type; return canvas.toDataURL("image/"+type, 0.7);//這裡的0.7值的是圖片的質量 } 複製程式碼
在上面的程式碼中,我們傳入的引數主要有image物件,圖片型別,圖片的最大寬度和最大高度。呼叫方法時填入圖片允許的最大寬度或者是最大的高度,進行等比繪製到canvas中,然後通過toDataURL來轉換成base64格式返回,此時的圖片就是壓縮過後的圖片。
建立image物件
上面的示例說了圖片壓縮的過程,其中有一個引數是image物件,那麼這個image物件是如何來的呢。
selectFileImage(el){ var reader = new FileReader(); var file = el.target.files[0] var fileName = file.name; var fileType = file.name.split(".")[1]; reader.readAsArrayBuffer(file); reader.onload = (ev) => { var blob = new Blob([ev.target['result']]); window['URL'] = window['URL'] || window['webkitURL']; var blobURL = window['URL'].createObjectURL(blob); var image = new Image(); image.src = blobURL; image.onload = (e) => { var thumb = this.resizeMe(image,fileType, 400, 0);//獲得的路徑是將圖片轉換成了base64 axios.post("http://127.0.0.1:3003/useasync/upload",{file:thumb,fileName:fileName}).then(res => { if (res.data.code == 200) { console.log(res) } else { console.log(res) } }); } } } 複製程式碼
在這裡面我使用了一個FileReader物件,FileReader 物件允許Web應用程式非同步讀取儲存在使用者計算機上的檔案(或原始資料緩衝區)的內容,使用 File 或 Blob 物件指定要讀取的檔案或資料。
當觸發input的onChange事件後,可以讀取到input中的file檔案,於是將此檔案讀取到快取當中,當讀取完成後,result屬性中儲存的將是被讀取檔案的ArrayBuffer資料物件。這裡我們可以看一下讀取完成後的這個ev到底是個什麼東西 我們可以看到這裡面loaded和total都表示的是檔案的總大小,重要的其實是target和currentTarget,這兩個屬性其實是一樣的,裡面包含了讀取的fileReader物件,裡面的result就是快取中的資料了,我們通過new 一個Blob物件,將其轉換為Blob物件,然後就可以通過url方法來將其轉換為可以放到img src中的連結形式了。此時建立image物件,並對其src進行賦值,當image載入完成後,就開始呼叫壓縮方法,傳入的image物件就是我們剛才生成的image物件。
當壓縮完成後返回的資料就是base64的資料了,我們就可以通過ajax非同步來進行上傳,在此我採用的是axios進行非同步上傳,將內容及檔名作為引數傳遞給後臺。
後臺接收
在這我才用的示例為nodejs搭建後臺來接收圖片,這裡我們需要一個bodyParser模組
app.use(bodyParser.json({ limit:'5mb'}));//限制允許提交的大小 複製程式碼
將大小限制為5M以內,也就是說通過base64上傳的圖片大小一定要小於5M才能成功,這個引數我們可以隨意更改,按業務需求而定。
router.post("/upload",function(req,res){ var imgData = req.body.file; var fileName =req.body.fileName; var base64Data = imgData.replace(/^data:image\/\w+;base64,/, ""); var dataBuffer = new Buffer(base64Data, 'base64'); fs.writeFile(process.cwd()+"/upload/"+fileName, dataBuffer, function(err) { if(err){ res.json({success:false,errormsg:err}); }else{ res.send({success:true,msg:"儲存成功!"}); } }); }) 複製程式碼
介面中我們通過Buffer來將base64轉換為buffer,進而儲存到伺服器本地中,本示例採用的就是將圖片儲存到伺服器本地。如此通過將base64編碼圖片儲存為圖片就做完了。
由於壓縮採用的是canvas,獲取檔案等是通過FileReader 物件及Bolb物件,故此方法目前的相容性最低為IE10,還請酌情使用。