移動端H5圖片上傳的那些坑

蘋果小蘿蔔發表於2016-08-02

上週做一個關於移動端圖片壓縮上傳的功能。期間踩了幾個坑,在此總結下。

大體的思路是,部分API的相容性請參照caniuse

  1. 利用FileReader,讀取blob物件,或者是file物件,將圖片轉化為data uri的形式。
  2. 使用canvas,在頁面上新建一個畫布,利用canvas提供的API,將圖片畫入這個畫布當中。
  3. 利用canvas.toDataURL(),進行圖片的壓縮,得到圖片的data uri的值
  4. 上傳檔案。

步驟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圖片上傳過程中,存在著這樣的問題:

  1. 當你豎著拿手機的時候,拍完照,上傳圖片時,會出現照片自動旋轉的情況,而橫著拍照並上傳圖片時不會出現這個問題。這個時候如果想糾正圖片自動旋轉的情況,將圖片轉化為二進位制的資料(使用了binaryajax.js),方便獲取圖片的exif資訊,通過獲取exif的資訊來確定圖片旋轉的角度(使用了exif.js),然後再進行圖片相應的旋轉處理。解決方法請戳我
  2. IOS中,當圖片的大小大於 2MB時,會出現圖片壓扁的情況,這個時候需要重置圖片的比例。解決方法請戳我
  3. 利用FileReader,讀取圖片的過程需要花費一定時間,將圖片資料注入到canvas畫布中需要一定時間,圖片壓縮的過程中,圖片越大,CPU計算消耗的時間也越長,可能會出現頓卡的情況。總之,就是這個過程當中需要花費一定時間。
  4. IOS8.1的版本中有個FileReader的bug: FileReader讀取的圖片轉化為Base64時,字串為空,具體的問題描述請戳我

步驟4,檔案上傳有2種方式:

  1. 將圖片轉化為base64
  2. 將圖片資料轉為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">由於系統WebViewopenFileChooser介面更改,導致無法選擇檔案,從而導致無法上傳檔案. bug描述請戳我

封裝好的github庫,請戳我,如果覺得文章不錯,請不要吝嗇你的star~~

相關文章