這裏是《整理前端開發中的可複用代碼》中的第二篇,最初此係列文章的標題不是這個,但以爲標題要準確、明白一些,便作了修改。這裏的經驗都來自做者的工做實踐,入了前端坑的摸爬滾打。php
在工做中接到一個需求,須要在瀏覽器端計算文件的md5值,而後在上傳文件時傳遞給後端儲存。按照以往的思路,後端只須要三兩行代碼便可實現,好比使用php中的md5_file。html
<?php
$filename = "file.jpg";
$md5 = md5_file($filename);
echo $md5;
?>
複製代碼
然而文件並非上傳到後端服務器,而是直接上傳到阿里雲的oss。若是後端去請求oss上的文件計算md5,就容易形成服務器的壓力,因此計算文件md5的最好方式是交給客戶端實現。前端
儘管之前沒有接觸過瀏覽器端計算文件md5,但在谷歌、百度搜索一番後,便用spark-md5解決了這一需求。但若是事情就這樣結束了的話,就沒有後續計算網絡文件md5的想法,以及這篇文章的出現了。ios
接着計算本地文件md5的需求,又接到了一個需求。上傳的圖片要進行壓縮,壓縮使用plupload的自帶功能實現,而壓縮後的圖片md5已改變。通過一些時間尚未找到解決方案,加上技術總監對我說項目後續要增長新東西,讓我最好在審覈上傳資源的時候計算md5,這樣便開始了前端計算網絡文件md5的摸索。git
在谷歌、百度了N久以後,沒有找到一個準確的答案,包括flash方案。我想應該不多有在瀏覽器端計算網絡文件md5的需求或實現,也展開了本身的一些猜測。github
第一應該先要有一個文件,而後從文件中計算md5,相似:web
var file = new File(['www.domain.com/test.jpg'], 'test.jpg', {type: 'image/jpg'});
複製代碼
然而File對象並不接收url參數來生成文件,這裏的url被當成字符串處理了,這不是想要的答案。後端
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();
複製代碼
經過上面的代碼,便實現了瀏覽器端js計算網絡文件的md5。而據spark-md5的使用方式,直接計算字符串md5時比較簡單:
SparkMD5.hash('hello world');//"5eb63bbbe01eeed093cb22bb8f5acdc3"
複製代碼
如上只須要一行代碼便可,但在計算文件時略複雜些,代碼不利於複用。出於本系列文章整理可複用代碼的初衷,以及想要加入計算網絡文件md5功能,便封裝了一個插件。
使用方式:
<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的任務主要分配給後端,但漸漸的前端技術也能實現了,這意味着前端技術的逐漸繁榮。不過用「痛並快樂着」很適合描述前端開發,使用一個個新技術完成了之前不能實現的功能,也面臨着各類瀏覽器兼容性問題帶來的困擾。
還願咱們心懷嚮往,並砥礪前行吧。
此係列相關文章(同步更新):