整理前端工作中的可複用程式碼(二):擴充spark-md5,支援計算網路檔案md5

霧空發表於2019-04-05

這裡是《整理前端開發中的可複用程式碼》中的第二篇,最初此係列文章的標題不是這個,但覺得標題要準確、明白一些,便做了修改。這裡的經驗都來自作者的工作實踐,入了前端坑的摸爬滾打。

背景

在工作中接到一個需求,需要在瀏覽器端計算檔案的md5值,然後在上傳檔案時傳遞給後端儲存。按照以往的思路,後端只需要三兩行程式碼即可實現,比如使用php中的md5_file。

<?php
  $filename = "file.jpg";
  $md5 = md5_file($filename);
  echo $md5;
?>
複製程式碼

然而檔案並不是上傳到後端伺服器,而是直接上傳到阿里雲的oss。如果後端去請求oss上的檔案計算md5,就容易造成伺服器的壓力,所以計算檔案md5的最好方式是交給客戶端實現。

儘管以前沒有接觸過瀏覽器端計算檔案md5,但在谷歌、百度搜尋一番後,便用spark-md5解決了這一需求。但如果事情就這樣結束了的話,就沒有後續計算網路檔案md5的想法,以及這篇文章的出現了。

接著計算本地檔案md5的需求,又接到了一個需求。上傳的圖片要進行壓縮,壓縮使用plupload的自帶功能實現,而壓縮後的圖片md5已改變。經過一些時間還沒有找到解決方案,加上技術總監對我說專案後續要增加新東西,讓我最好在稽核上傳資源的時候計算md5,這樣便開始了前端計算網路檔案md5的摸索。

File 物件

在谷歌、百度了N久之後,沒有找到一個準確的答案,包括flash方案。我想應該很少有在瀏覽器端計算網路檔案md5的需求或實現,也展開了自己的一些猜想。

第一應該先要有一個檔案,然後從檔案中計算md5,類似:

var file = new File(['www.domain.com/test.jpg'], 'test.jpg', {type: 'images/jpg'});
複製程式碼

然而File物件並不接收url引數來生成檔案,這裡的url被當成字串處理了,這不是想要的答案。

Blob 物件

Blob物件是一個類檔案物件,File物件繼承於Blob物件,它們的用法類似,Blob也不能直接處理url為Blob物件。但要計算檔案md5,需要FileReader讀取一個File或Blob物件,再由spark-md5進一步計算得出md5。

所幸在XMLHttpRequest中可以指定responseType為blod,請求檔案並指定responseType為blod時,XMLHttpRequest將返回一個Blob物件,相關介紹可參考這裡responseType,如下:

var request = new XMLHttpRequest();

request.open('GET', url, true);
request.responseType = 'blob';
request.onload = function() {
  //類似Blob(3275) {size: 3275, type: "image/vnd.microsoft.icon"}
  console.log(request.response);
};
request.send();
複製程式碼

至此,下一步便可以用spark-md5計算Blob物件來返回md5了(spark-md5官方示例)。

var request = new XMLHttpRequest();

request.open('GET', url, true);
request.responseType = 'blob';
request.onload = function() {
 var file = request.response;

var blobSlice = File.prototype.slice || File.prototype.mozSlice || 
    File.prototype.webkitSlice,
    chunkSize = 2097152, // Read in chunks of 2MB
    chunks = Math.ceil(file.size / chunkSize),
    currentChunk = 0,
    spark = new SparkMD5.ArrayBuffer(),
    fileReader = new FileReader();

fileReader.onload = function (e) {
    spark.append(e.target.result);
    currentChunk++;

    if (currentChunk < chunks) {
        loadNext();
    } else {
    	//獲取md5
        var md5 = spark.end();
        console.log(md5);
    }
};

fileReader.onerror = function () {
    console.error('檔案讀取失敗');
};

function loadNext() {
    var start = currentChunk * chunkSize,
        end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;

    fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
}

loadNext();
};
request.send();
複製程式碼

擴充spark-md5

通過上面的程式碼,便實現了瀏覽器端js計算網路檔案的md5。而據spark-md5的使用方式,直接計算字串md5時比較簡單:

SparkMD5.hash('hello world');//"5eb63bbbe01eeed093cb22bb8f5acdc3"
複製程式碼

如上只需要一行程式碼即可,但在計算檔案時略複雜些,程式碼不利於複用。出於本系列文章整理可複用程式碼的初衷,以及想要加入計算網路檔案md5功能,便封裝了一個外掛。



md5-util

使用方式:

<script src="md5-util.min.js"></script>
複製程式碼
//計算本地檔案md5
SparkMD5.file(file,function(md5){
 console.log(md5)
})

//計算網路檔案md5
SparkMD5.file(url,function(md5){
 console.log(md5)
})
複製程式碼

這裡參考過browser-md5-file,不同的是此外掛只擴充套件了spark-md5,增加了file方法,spark-md5的所有方法均可在外掛中使用。

瀏覽器相容性

這裡只討論計算網路檔案md5的相容性,在pc端一些主流瀏覽器中測試了多次,得出的md5均是正確的。但由於XMLHttpRequest的responseType指定為blob,在移動端發現一些相容性問題,已知ios uc瀏覽器及安卓5.1.1系統瀏覽器中返回blob異常,導致md5計算錯誤。

所以要計算網路檔案的md5時,請慎用。如果哪位大神有更好的解決辦法,還請分享下。

後記

一直以來web端計算md5的任務主要分配給後端,但漸漸的前端技術也能實現了,這意味著前端技術的逐漸繁榮。不過用“痛並快樂著”很適合描述前端開發,使用一個個新技術完成了以前不能實現的功能,也面臨著各種瀏覽器相容性問題帶來的困擾。

還願我們心懷嚮往,並砥礪前行吧。


此係列相關文章(同步更新):

  1. 整理前端工作中的可複用程式碼(一):做一個整合儲存的外掛
  2. 整理前端工作中的可複用程式碼(二):擴充spark-md5,支援計算網路檔案md5

相關文章