前端上傳圖片到服務器,通常會有兩種方式:前端
1. Base64編碼nginx
2. FormData文件流canvas
首先,若是圖片轉爲 Base64 後,除了體積會增大33%左右外,更大的問題是,你覺得你是在以字符串的格式傳輸二進制。 其實非也,尤爲是你把它轉爲 JSON 傳給後端,因爲變成了須要解析的字符串,對後端的壓力會陡增,你會明顯看處處理速度的急劇下滑。以及,你還須要去調 nginx 配置文件以免 nginx 以爲你字符串過長,直接報錯 Entity Too Large
把包拒掉。後端
因此,Base64 更適合用來處理小圖,好比頭像或小圖標之類,保存在本地,以減小 Http 的請求次數;而大圖比較適合用 FormData 文件流來傳輸。服務器
而我最近的需求是把手機拍照的圖片上傳到服務器,因此肯定了用文件流來處理。app
而咱們的應用是通過 Hbuilder 打包後的 Web App, 加持了 iOS 和 Android 的原生 SDK 的功能,因此面臨着兩個選擇:函數
<input>
來拍照及選取相冊若是用原生 <input>
,能夠這麼寫(項目用的是 Vue):ui
<input type="file" accept="image/*" capture="camera" @change="onChange" multiple>
編碼
實際上呢,坑還很多。。。code
加上capture="camera"
這個屬性,是爲了讓手機可選相機,不過真實狀況是:
部分 Android 手機上調用不了相機,始終只能選相冊;(解決辦法:請教 Android 原生開發的童鞋吧)
蘋果手機上,只能拍照,不能選相冊;(解決辦法:判斷 iOS,若是是 iOS 就去掉 capture 屬性)
因此, Android 不能調用相機這點,就把 Web 原生的方案斃掉了,後來轉用 HBuilder 的 SDK;
接下來的大體思路:
獲取到 File 對象
FileReader 轉成 dataUrl
canvas 接收 dataUrl
對圖片進行寬高比的設置(壓縮大小)
再canvas.toDataURL
將壓縮後的圖片生成新的 Base64 編碼
執行回調函數,將新的 Base64 轉換爲文件
最後拿到文件,構造 FormData 對象上傳至服務器。
特別要注意的是:
FormData 的請求方式,每一個參數都須要經過 append 方法添加進去;
不須要單獨設置請求頭的 Content-Type
,系統自動會設定爲 multipart/form-data
通過幾天線上的驗證,事實證實,只用文件流傳圖片,有時也會報 Entity too large
這個錯誤,後來通過定位發如今 canvas.toDataURL
時,最好用 jpeg 格式進行壓縮,不要用 png 的無損壓縮,這點很是關鍵!
---The End