這裡是《整理前端開發中的可複用程式碼》中的第二篇,最初此係列文章的標題不是這個,但覺得標題要準確、明白一些,便做了修改。這裡的經驗都來自作者的工作實踐,入了前端坑的摸爬滾打。
背景
在工作中接到一個需求,需要在瀏覽器端計算檔案的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功能,便封裝了一個外掛。
- github地址:md5-util
- 演示:md5-util demo,下方附二維碼
使用方式:
<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的任務主要分配給後端,但漸漸的前端技術也能實現了,這意味著前端技術的逐漸繁榮。不過用“痛並快樂著”很適合描述前端開發,使用一個個新技術完成了以前不能實現的功能,也面臨著各種瀏覽器相容性問題帶來的困擾。
還願我們心懷嚮往,並砥礪前行吧。
此係列相關文章(同步更新):