上週做一個關於移動端圖片壓縮上傳的功能。期間踩了幾個坑,在此總結下。
大體的思路是,部分API的相容性請參照caniuse:
- 利用FileReader,讀取
blob物件
,或者是file物件
,將圖片轉化為data uri
的形式。 - 使用
canvas
,在頁面上新建一個畫布,利用canvas
提供的API,將圖片畫入這個畫布當中。 - 利用
canvas.toDataURL()
,進行圖片的壓縮,得到圖片的data uri
的值 - 上傳檔案。
步驟1當中,在進行圖片壓縮前,還是對圖片大小做了判斷的,如果圖片大小大於200KB時,是直接進行圖片上傳,不進行圖片的壓縮,如果圖片的大小是大於200KB,則是先進行圖片的壓縮再上傳:
<input type="file" id="choose" accept="image/*">
var fileChooser = document.getElementById("choose"),
maxSize = 200 * 1024; //200KB
fileChoose.change = function() {
var file = this.files[0], //讀取檔案
reader = new FileReader();
reader.onload = function() {
var result = this.result, //result為data url的形式
img = new Image(),
img.src = result;
if(result.length < maxSize) {
imgUpload(result); //圖片直接上傳
} else {
var data = compress(img); //圖片首先進行壓縮
imgUpload(data); //圖片上傳
}
}
reader.readAsDataURL(file);
}
步驟2,3:
var canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d');
function compress(img) {
canvas.width = img.width;
canvas.height = img.height;
//利用canvas進行繪圖
//將原來圖片的質量壓縮到原先的0.2倍。
var data = canvas.toDataURL('image/jpeg', 0.2); //data url的形式
return data;
}
在利用canvas進行繪圖的過程中,IOS圖片上傳過程中,存在著這樣的問題:
- 當你豎著拿手機的時候,拍完照,上傳圖片時,會出現照片自動旋轉的情況,而橫著拍照並上傳圖片時不會出現這個問題。這個時候如果想糾正圖片自動旋轉的情況,將圖片轉化為二進位制的資料
(使用了binaryajax.js)
,方便獲取圖片的exif資訊
,通過獲取exif的資訊
來確定圖片旋轉的角度(使用了exif.js)
,然後再進行圖片相應的旋轉處理。解決方法請戳我 - 在
IOS
中,當圖片的大小大於 2MB時,會出現圖片壓扁的情況,這個時候需要重置圖片的比例。解決方法請戳我 - 利用FileReader,讀取圖片的過程需要花費一定時間,將圖片資料注入到canvas畫布中需要一定時間,圖片壓縮的過程中,圖片越大,CPU計算消耗的時間也越長,可能會出現頓卡的情況。總之,就是這個過程當中需要花費一定時間。
- IOS8.1的版本中有個
FileReader
的bug:FileReader
讀取的圖片轉化為Base64時,字串為空,具體的問題描述請戳我
步驟4,檔案上傳有2種方式:
- 將圖片轉化為base64
- 將圖片資料轉為Blob物件,使用FormData上傳檔案
方式1可以通過xhr ajax或者xhr2 FormData進行提交。
方法2這裡就有個大坑了。具體描述請戳我
簡單點說就是:Blob物件
是無法注入到FormData物件
當中的。
當你拿到了圖片的data uri資料
後,將其轉化為Blob
資料型別
var ndata = compress(img);
ndata = window.atob(ndata); //將base64格式的資料進行解碼
//新建一個buffer物件,用以儲存圖片資料
var buffer = new Uint8Array(ndata.length);
for(var i = 0; i < text.length; i++) {
buffer[i] = ndata.charCodeAt(i);
}
//將buffer物件轉化為Blob資料型別
var blob = getBlob([buffer]);
var fd = new FormData(),
xhr = new XMLHttpRequest();
fd.append('file', blob);
xhr.open('post', url);
xhr.onreadystatechange = function() {
//do something
}
xhr.send(fd);
在新建Blob物件
中有需要進行相容性的處理,特別是對於不支援FormData
上傳blob
的andriod機的相容性處理。具體的方法請戳我
主要實現的細節是通過重寫HTTP請求。
2月19日更新
在安卓機器中,部分4.x
的機型, 在webview
裡面對file
物件進行了閹割,比如你拿不到file.type
的值。
2月22日更新
Android4.4
下<input type="file">
由於系統WebView
的openFileChooser
介面更改,導致無法選擇檔案,從而導致無法上傳檔案. bug描述請戳我