整理前端工做中的可複用代碼(二):拓展spark-md5,支持計算網絡文件md5

這裏是《整理前端開發中的可複用代碼》中的第二篇,最初此係列文章的標題不是這個,但以爲標題要準確、明白一些,便作了修改。這裏的經驗都來自做者的工做實踐,入了前端坑的摸爬滾打。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

File 對象

在谷歌、百度了N久以後,沒有找到一個準確的答案,包括flash方案。我想應該不多有在瀏覽器端計算網絡文件md5的需求或實現,也展開了本身的一些猜測。github

第一應該先要有一個文件,而後從文件中計算md5,相似:web

var file = new File(['www.domain.com/test.jpg'], 'test.jpg', {type: 'image/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
相關文章
相關標籤/搜索