檔案上傳那些事兒

qcloud發表於2017-05-06

最近M端專案中涉及到圖片上傳功能,現把專案中遇到的一些問題及解決辦法分享如下,與各位共同探討:

PS:文章已同步至個人部落格

一、相關需求:

1. 客戶端上限10M
2. 伺服器端上限2M
3. 檔案過濾
4. 顯示上傳進度
5. 非同步上傳
6. 多檔案上傳

二、需求分析:

1. 實現2,可使用canvas在前端實現壓縮(base64);
2. 實1、3,可採用file.size及/image/.test(file.type)過濾;
3. 實現4,使用XHR2實現上傳,新增進度時間監控,xhr.upload.addEventListener(“progress”, uploadProgress, false);
4. 實現5,使用XHR2實現上傳;
5. 多檔案上傳,單檔案迴圈上傳即可,但是相容進度時,需單檔案各自監控;

三、需求實現:

實現一:二進位制方式上傳
需求1、3、4、5、6皆可實現,但是伺服器端上傳2M,使用canvas方式壓縮後生成的是base64,若使用此方式上傳,必須把base64轉換成二進位制流,GitHub上也有相關文章把base64轉換成二進位制流的,使用xhr.sendAsBinary()傳送二進位制流,參考此文,測了一部分常見機型,可以實現,具體是否可以在專案中使用還有待論證。二進位制上傳實現部分程式碼僅供參考:

var uploadFile = function(fileid, file) {
  var xhr = new XMLHttpRequest(),
      fd = new FormData,
      url = "/picture/upload",
      // 上傳進度
      uploadProgress = function(evt) {
        if (evt.lengthComputable) {
          var percent = (evt.loaded * 100 / evt.total).toFixed(1);
          // show percent
        }else {
          console.log('unable to compute');
        }
      },
      uploadFailed = function(evt) {
        // 上傳失敗
      },
      uploadCanceled = function(evt) {
        // 取消上傳或網路連線斷開!
      };

  xhr.upload.addEventListener("progress", uploadProgress, false);
  xhr.addEventListener("error", uploadFailed, false);
  xhr.addEventListener("abort", uploadCanceled, false);
  xhr.open("POST", url , true);
  xhr.onreadystatechange = function(e) {
    if (xhr.readyState == 4) {
      if (xhr.status === 200) {
        var data = xhr.response;
        if (data != 0) {
          // 上傳失敗
        } else {
          // 上傳成功
        }
      } else {
        // 上傳失敗
      }
    }
  };

  fd.append(fileid, file);
  xhr.send(fd);
};

實現二:base64上傳

需求1、2、3、5、6皆可實現,實現此種方式即基本的Get上傳,但是無法實時監控上傳檔案進度,需求4無法實現。

實現三:二進位制+base64
即上述兩種方案的綜合。也可參考此文移動端Web上傳圖片實踐中的例項。

四、問題總結:

M端瀏覽器各異,支援情況各異,現總結如下:
a) 部分酷派機型瀏覽器(微信、UC、QQ、百度),中興自帶瀏覽器不支援input[type=file];
解決方式:放棄

b) Adroid機型,不同瀏覽器對input[type=file]支援不同,有的沒有相簿選項,有的沒有相機選項。主要表現為小米、酷派部分機型的微信自帶瀏覽器。
解決方式:input[type=file]新增accept=’image/*’屬性,可實現某些adriod機型不出現文件選項。

c) 上傳檔案時,出現圖片自動旋轉的問題
解決方式:實現開源外掛CanvasResize中exif.js來糾正,實現此外掛可解決壓縮、糾正圖片旋轉,但Adroid上UC瀏覽器中會出現下圖問題:(國外人寫的外掛哪會管國內瀏覽器死活!)
uc-bug
最後採用的騰訊的一款壓縮方案,解決了UC瀏覽器的問題。

d) 使用壓縮外掛時需注意,PNG圖片壓縮時往往會偏大,可把壓縮成image/jpeg格式;
var cvs = document.createElement(‘canvas’);
var ctx = cvs.getContext(“2d”).drawImage(source_img_obj, 0, 0);
var newImageData = cvs.toDataURL(‘image/jpeg’, quality/100);

f) 因瀏覽器對input[type=file]顯示風格各異,專案使用label的for指向input[type=file]的id,並設定input{display:none};在Adroid部分瀏覽器上點選無反應;
解決方式:設定input{position: absolute; top: -99em;}來隱藏。

g) 在部分Adroid支援input[type=file]的瀏覽器中,當使用/image/.test(file.type)時,選擇圖片檔案會返回false。使用JSON.stringify(file)分析後發現,是file物件中的name欄位中沒有包含字尾,同時type欄位為空,使用this.value獲取路徑中也沒有包含字尾。因此過濾出現問題。
如下結果:

1 {“webkitRelativePath”:””,”lastModified”:1433304214000,”lastModifiedDate”:”2015-06-03T04:03:34.000Z”,“name”:”fanmian”,”type”:””,”size”:2273852}

正常結果如下:

1 {“webkitRelativePath”:””,”lastModified”:1433304214000,”lastModifiedDate”:”2015-06-03T04:03:34.000Z”,“name”:”fanmian.png”,”type”:”image/png”,”size”:2273852}

解決方式:放開/image/.test(file.type)過濾,在壓縮時,丟擲錯誤過濾。

h) html5上傳檔案,Firefox支援重複選擇同一檔案,其它瀏覽器不支援
解決方式:每次選擇檔案後給input[type=file]賦值空。

文中不妥之處,歡迎批評指正!

五、參考連結:

Html5 File Upload with Progress
移動端Web上傳圖片實踐

圖片壓縮成base64,採用二進流上傳

相關文章