背景:移動端H5項目,需要實現調用手機拍照,並將圖片壓縮上傳功能。
- 頁面樣式:
- 上傳圖片有原生的方法<input type="file" accept="image/*">,如果只想要拍照上傳,不希望用戶選擇圖片上傳,可以通過添加capture屬性,該屬性值有camcorder/microphone/camera...,此處選擇camera。PS:該屬性存在瀏覽器兼容性問題,不是所有的瀏覽器都支持。
<input type="file" accept="image/*" capture="camera" >
- 因為原生file樣式不滿足要求,需要進行相應的處理,此時使用定位,在input上面放置我們想要的頁面效果。然后當點擊上面的元素,就可以觸發我們的input進行圖片上傳。此時的問題是:當點擊input上面的元素,需要事件穿透,即相當於點擊input。則借助於css3新屬性pointer-events。
//使用cursor進行事件穿透,來阻止元素成為鼠標事件的目標 button{ cursor:pointer; pointer-events:none; }
----此時圖片上傳的樣式已經處理好了----
代碼片段:<style > *{ padding: 0; margin: 0; } .wrapper{ width: 320px; height: 50px; margin: 20px auto; position: relative; border: 1px solid #f0f0f0; } input{ width: 100px; height: 30px; } button{ position: absolute; cursor: pointer; pointer-events: none; width: 100px; height: 30px; left: 0; top: 0; } a{ pointer-events: none; } .img{ border: 1px solid #ccc; padding: 10px; } </style > <div class = "wrapper"> <input type = "file" accept= "image/*" capture= "camera" id= "img" /> <button >上傳照片 </button > </div >
- 上傳圖片有原生的方法<input type="file" accept="image/*">,如果只想要拍照上傳,不希望用戶選擇圖片上傳,可以通過添加capture屬性,該屬性值有camcorder/microphone/camera...,此處選擇camera。PS:該屬性存在瀏覽器兼容性問題,不是所有的瀏覽器都支持。
- 圖片壓縮處理:
- 因為要做的是手機拍照上傳,現在的手機拍照片都很大,比如小米4S,大小在3M以上,如果原圖上傳,太消耗用戶流量,於是要解決圖片壓縮的問題。
-
通過change事件,監聽圖片上傳,通過readerAsDataURL獲取上傳的圖片。
document.getElementById( 'img').addEventListener( 'change', function () { var reader = new FileReader(); reader.onload = function (e) { //調用圖片壓縮方法:compress(); }; reader.readAsDataURL(this.files[0]); console.log(this.files[0]); var fileSize = Math.round( this.files[0].size/1024/1024) ; //以M為單位 //this.files[0] 該信息包含:圖片的大小,以byte計算 獲取size的方法如下:this.files[0].size; }, false);
-
對上傳的圖片進行壓縮,需要借助於canvas API,調用其中的canvas.toDataURL(type, encoderOptions); 將圖片按照一定的壓縮比進行壓縮,得到base64編碼。重點來了:壓縮策略:先設置圖片的最大寬度 or 最大高度,一般設置其中一個就可以了,因為所有的手機寬高比差別不是很大。然后設置圖片的最大size,allowMaxSize,根據圖片的實際大小和最大允許大小,設置相應的壓縮比率。
//最終實現思路: 1、設置壓縮后的最大寬度 or 高度; 2、設置壓縮比例,根據圖片的不同size大小,設置不同的壓縮比。 function compress(res,fileSize) { //res代表上傳的圖片,fileSize大小圖片的大小 var img = new Image(), maxW = 640; //設置最大寬度 img.onload = function () { var cvs = document.createElement( 'canvas'), ctx = cvs.getContext( '2d'); if(img.width > maxW) { img.height *= maxW / img.width; img.width = maxW; } cvs.width = img.width; cvs.height = img.height; ctx.clearRect(0, 0, cvs.width, cvs.height); ctx.drawImage(img, 0, 0, img.width, img.height); var compressRate = getCompressRate(1,fileSize); var dataUrl = cvs.toDataURL( 'image/jpeg', compressRate); document.body.appendChild(cvs); console.log(dataUrl); } img.src = res; } function getCompressRate(allowMaxSize,fileSize){ //計算壓縮比率,size單位為MB var compressRate = 1; if(fileSize/allowMaxSize > 4){ compressRate = 0.5; } else if(fileSize/allowMaxSize >3){ compressRate = 0.6; } else if(fileSize/allowMaxSize >2){ compressRate = 0.7; } else if(fileSize > allowMaxSize){ compressRate = 0.8; } else{ compressRate = 0.9; } return compressRate; }
- 圖片上傳給服務器:
- 圖片已經壓縮完成了,但是壓縮后的圖片不是File,而是一個base64編碼,於是乎兩個選擇:1、以String的形式將base64編碼上傳給服務器,服務器轉存base64為img圖片;2、前端將base64轉為File圖片類型,上傳給服務器。
- 方法一:壓縮之后直接上傳base64給后台,實現簡單。
- 方法二:前端自己轉存base64位File圖片,這個對於前端 壓力比較大。
原因:html5 canvas屬於客戶端API,沒有權限去保存圖片到硬盤,只有canvas.toDataURL()這一接口可導出畫布的base64編碼,以提供給服務器進行處理保存。所以如果要將base64編碼轉成圖片需要借助於nodeJs。因為nodeJS有相關文件處理的API。
//使用nodeJS將base64轉化成圖片 var express = require('express'); var fs = require("fs"); var app = module.exports = express(); function dataToImage(dataUrl){ var base64Data = dataUrl.replace(/^data:image\/\w+;base64,/,''); var dataBuffer = new Buffer(base64Data,'base64'); fs.writeFile('out.jpg',dataBuffer,function(err){ if(err){ console.log(err); }else{ console.log('Success...'); } }); } dataToImage('data:image/jpeg;base64,/9...'); //圖片完整base64過長,所以省略... if(!module.parent){ app.listen(8000); console.log('Express started on port 8000'); }
Summary:如果使用nodeJS,需要單獨部署nodeJS代碼到服務器,整個邏輯會比較麻煩。綜合比較兩種方法,推薦使用第一種方法,直接傳base64給服務器,后台處理相應的轉化
-
相關知識科普:
- 圖像一般由兩部分組成:一部分是數據本身,他記錄了每個像素的顏色值,另一部分是文件頭,這里記錄着形如圖像的寬度,高度等信息。
- 不同圖片類型及適用場景:
- 不同圖片類型:
- GIF:無損壓縮,體積小,支持透明效果,缺點:色彩效果最低,用gif保存鮮艷的圖片的話會讓網站看上去非常可怕。
- PNG:無損壓縮,可漸變透明,缺點:不如JPG顏色豐富,同樣的圖片體積也比JPG略大。
- JPG/JPEG:色彩還原性好,可以在照片不明顯失真的情況下,大幅度壓縮圖片格式,所以體積不會很大。缺點:不支持透明
- 適用場景:
- 當圖片色彩鮮艷,基本沒有透明效果的時候,選擇jpg/jpeg。
- 當圖片色彩鮮艷,透明效果較多的情況下,選擇png;
- 當圖片色彩比較單一情況下,可以選擇gif;
- 不同圖片類型:
- toDataURL,查找原生壓縮圖片的方法。type支持image/webp格式
canvas.toDataURL(type, encoderOptions);
- Base64編碼:將三個8Bit的字節轉換為四個6Bit的字節。