使用base64進行移動端圖片上傳
之前搞微信上的圖片上傳,想直接通過介面上傳到自己的圖片伺服器,發現移動端瀏覽器上挺多坑的,使用最簡單的form-data形式好像不成。研究了一下,發現base64格式通用性較強。
base64編碼
base64是一種使用可列印字元來描述二進位制資料的方法。base64字符集共有64個字元,包括a-zA-Z0-9共62個,另外兩個符號為+和/。
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
由於26=64,因而該64個字元只能描述6bits的資料,換句話說,3個位元組有24bits,對應著4個base64字元。在做base64編碼時,一般要在位元組流最後補充一到兩個0,相應的,編碼完成後,要在base64串最後補充一到兩個=。
解碼
解碼就是根據base64串還原二進位制資料的過程。
圖片顯示
img標籤是可以直接顯示base64編碼的圖片,像下面這樣:
<img src="data:image/gif;base64,R0lGODlhAwADAIABAL6+vv///yH5BAEAAAEALAAAAAADAAMAAAIDjA9WADs=" />
圖片上傳
圖片上傳可以通過form-data/Blob物件等方式,但針對不同的瀏覽器可能會出現適配上的問題。
如果將圖片轉化為base64格式,得到的就是一個字串,可以通過POST上傳到伺服器。所有瀏覽器對POST方法肯定是支援的,這個辦法通用性比較強。美中不足的是,圖片通常比較大,POST請求本身對資料大小是沒有限制的,但是一般後臺會限制上傳資料大小
在前端,可以使用FileReader得到本地圖片的base64編碼。程式碼如下:
HTML(圖片選擇框):
<input class="select" type="file" accept="image/*" onchange="change(this)">
js:
function change (obj) {
if (!obj.files.length) return;
if (obj.files.length > 1) {
alert("只允許上傳一張圖片!");
return ;
}
var file = obj.files[0];
var reader = new FileReader();
reader.onload = function () {
var result = this.result; //data:base64
$.post('/upload', {'base64': result}, function(result) {
});
};
reader.readAsDataURL(file);
}
後端接收
後端使用connect-multiparty中介軟體。
安裝
npm install --save connect-multiparty
後端接收程式碼:
var multipart = require('connect-multiparty');
var multipartMiddleware = multipart();
router.post('/', multipartMiddleware, function(req, res, next) {
var base64Data = req.body.base64.replace(/^data:image\/.*;base64,/, "");//remove head
var ext = req.body.base64.match((/^data:([A-Za-z-+\/]+);base64)[1];
var target_path = 'out.' + ext;
fs.writeFile(target_path, base64Data, 'base64', function(err) {
if (!err) {
res.json({'msg':'success'});
}
});
});
至此,一個完整的前端圖片上傳完成。而且由於是採用了base64編碼來實現,包括微信、移動在內各個瀏覽器都能適應。
前端壓縮
在移動端,手機拍照圖片會比較大,可以使用canvas的toDataURL介面進行壓縮。
對於前端壓縮,可以參考此文,其程式碼也在github上。
為了應對IOS上canvas大小的限制,該程式碼採用了瓦片繪製,就是將原圖切割成一個一個的矩形來繪製的。在使用時候,邊界沒處理好,有些圖片壓縮後有明顯的痕跡。
有時間的話,可以對此進行優化。