超大文件如何計算md5?


首先,至少沒必要先把整個文件讀到內存里。比如在 php 里,如果有人 md5(file_get_contents(big_file_name)) 就確實非常不妥當。

因為 md5 是每 512 bit 作為一個 chunk 進行計算的。所以可以每次讀取一部分的內容(最少 512 bit,比較合適是 st_blksize),進行那些 chunk 部分的計算,之後再讀取下一部分內容繼續計算。


MD5演算法本身是分塊的,其他很多類似的演算法比如SHA-1也是的,所以可以支持流式計算,讀一塊算一塊,最後再一次性生成完整hash,完全沒有內存爆炸的可能。

大多數語言都會提供流式的HashAlgorithm的API的,php也提供了md5_file,而且查文檔看它內部是流式的。


用python自帶的hashlib模塊很容易實現啊

def MD5(file):
計算文件的MD5值
import hashlib
md5_value = hashlib.md5()
with open(file,"rb") as file:
while True:
data = file.read(2048)#每次只讀取2048位元組
if not data:
break
md5_value.update(data)#更新MD5值
return md5_value.hexdigest()

把文件分成一個個chunk再計算就不會塞爆內存了

_(=з」∠)_


簡單先說下,md5是有規範的,提供了現成的演算法(規範的名字就是md5演算法。RFC 1321 The MD5 Message-Digest Algorithm),我們只需要翻譯成c、java、python、js等等代碼。

代碼建議從網上找,沒必要造輪子。

另外,下載個校驗器,測試下代碼正確性,之前踩過坑。。

http://www.freesoft.org/CIE/RFC/1321/


我之前的做法是取每100m的前1m作md5.然後整體再md5一次。感覺也是蠻萌的。時間少花了很多,暫時還未碰撞過。


如下,可選擇支持的hash演算法。

python 3.6x通過測試,尚未進行其它版本的測試

import hashlib

def hashs(fineName, type="sha256", block_size=64 * 1024):
""" Support md5(), sha1(), sha224(), sha256(), sha384(), sha512(), blake2b(), blake2s(),
sha3_224, sha3_256, sha3_384, sha3_512, shake_128, and shake_256
"""
with open(fineName, rb) as file:
hash = hashlib.new(type, b"")
while True:
data = file.read(block_size)
if not data:
break
hash.update(data)
return hash.hexdigest()

# Copyright ? 2017 &
# Licensed under the MIT License. &

速度比靜態語言慢!


前端算超大文件可以取頭跟尾chunk內容及整個文件的name + update 時間一起算md5值就比較快了,只是為了做唯一標識來做斷點續傳,從業務邏輯上應該夠用了。

推薦使用 js spark-md5 開源庫,支持直接append各個部分然後算出md5。我做的斷點續傳功能就是用它在前端算的md5.


//使用openssl的寫法

bool Md5FileCpp(const std::string filepath,std::string strMd5)

{

FILE *pFile = fopen (filepath.c_str(), "rb");

if (pFile == NULL)

{

return false;

}

MD5_CTX ctx;

unsigned char buffer[1024] = {0};

int len = 0;

unsigned char md[16];

MD5_Init(ctx);

while ((len = fread (buffer, 1, 1024, pFile)) &> 0)

{

MD5_Update (ctx, buffer, len);

}

MD5_Final(md, ctx);

char buf[33] = { };

char tmp[3] = { };

for (int i = 0; i &< 16; i++)

{

sprintf_s(tmp,3,"%02x", md[i]);

strcat_s(buf,33,tmp);

}

strMd5.assign(buf, 33);

return true;

}


補充前端計算的方法,webuploader 支持分片計算md5,設置的分片不要太大就好了。

WebUploader.Uploader.register({
// 分片發送之前
before-send: beforeSend
}, {
beforeSend: function (block) {
let file = block.file;
let owner = this.owner;
owner.md5File(file.source, block.start, block.end).then(function(ret) {
console.log(ret)
});
},
...


md5_file - Manual 現成的函數


如果對一個1G大小的文件做md5,耗時是大概是什麼級別的


http://www.atool.org/file_hash.php js寫的文件hash,看他的代碼,你就知道怎麼計算大文件md5了…

肯定不是一次讀進來,不然瀏覽器爛爆了…


這個問題其實可以參考各大網盤是怎麼做的


據我猜測 各大網盤 TB級別 md5演算法應該是這樣的,樓上幾位都說了文件md5是文件流分塊算出來的,那麼網盤想獲得TB級別文件的md5就必須讀取整個文件的文件流才能得到,但是這麼做效率十分低下,運算時間是個問題。但是大家忽略了一個問題,文件在上傳的過程也是分塊上傳的,這些上傳的碎片其實也是文件流。那麼可以把計算md5的時間分攤到每一個碎片上。這樣每上傳一個片段就計算一點等上傳完成了,文件的md5也就算出來了。okTB級別MD5不是問題了。上傳完成md5自然就出來了。 不知道我的猜測大家有其他看法沒有。

剛才有仁兄提出都傳完了就還怎麼秒傳。秒傳最基本的是先要前端算出md5然後傳給後端(可能需要更多種哈希值)我研究了很久前端沒有辦法秒內完成超大文件MD5的,現在用html5 的api 可以算出任意大小文件的 md5 但是耗時相當長。我沒有解決辦法。也沒有想到那些網盤怎麼在前端快速獲取md5的。 有想方法或者感興趣的可以加我QQ 122138299一起研究一下。最近正在做斷點續傳 和秒傳的項目。


推薦閱讀:

Chrome DevTools: 在 Profile 性能分析中顯示原生 JS 函數
Runtime, Engine, VM 的區別是什麼?
在不使用node的情況下,開發者怎樣在js里調用一個自己實現的c/c++函數?
新手應該如何讀Google V8引擎源代碼?
js中為什麼沒有自乘自除?只有自加自減,為什麼?

TAG:Python | C# | PHP開發 | V8 | MD5 |