引言
在過去的幾年裡,wasm的話題那真是從早上聊到晚上,可以說處於異常興奮的狀態,但是幾年過去了,它慢慢的被大多數人們忘記,原因比較簡單——落地難
今天就wasm能給js加多少分這個問題,做一個小型的討論,今天的專注點是,前端js獲取一個檔案的md5值,也就是上傳檔案時所需要的秒傳功能的核心
簡單來說,檔案上傳秒傳不僅僅是網路硬碟公司的專屬,平時我們上傳檔案給後端也是很常用的,前端通過對目標檔案md5計算後與後端進行對比,如果已經上傳過,則直接返回已有地址,這樣,大大節省了伺服器空間。基本思路如下:
- 前端input type="file"獲取檔案
- 通過md5工具庫進行計算,得到md5值
- 請求介面,後端判斷此md5是否已經在資料庫裡
- 如果在資料庫裡,則直接告訴前端,已存在(秒傳)
本文重點
今天的重點是如何快速獲取一個檔案的md5值,這裡就涉及到小檔案,大檔案的問題了。所以,我將以下面檔案體積為例來測試js與wasm對檔案md5計算的速度對比。
wasm我使用golang進行開發,因為golang打包成wasm會把執行時也加進去,所以,打包的結果2.2M,我們暫時忽略這個體積,因為如果能落地,那麼換成rust,換成c++都不是難事,如果不能落地,那麼,golang不行,c++也照樣不行。
準備工作
通過ffmeg 從一個2G+的檔案上擷取不同體積的檔案,用於測試。
ffmpeg -i /path/sourch.mp4 -fs 1M -c:v copy -c:a copy /path/1M.mp4
ffmpeg -i /path/sourch.mp4 -fs 5M -c:v copy -c:a copy /path/5M.mp4
ffmpeg -i /path/sourch.mp4 -fs 20M -c:v copy -c:a copy /path/20M.mp4
ffmpeg -i /path/sourch.mp4 -fs 50M -c:v copy -c:a copy /path/50M.mp4
ffmpeg -i /path/sourch.mp4 -fs 100M -c:v copy -c:a copy /path/100M.mp4
ffmpeg -i /path/sourch.mp4 -fs 200M -c:v copy -c:a copy /path/200M.mp4
ffmpeg -i /path/sourch.mp4 -fs 400M -c:v copy -c:a copy /path/400M.mp4
ffmpeg -i /path/sourch.mp4 -fs 600M -c:v copy -c:a copy /path/500M.mp4
ffmpeg -i /path/sourch.mp4 -fs 800M -c:v copy -c:a copy /path/800M.mp4
ffmpeg -i /path/sourch.mp4 -fs 900M -c:v copy -c:a copy /path/900M.mp4
ffmpeg -i /path/sourch.mp4 -fs 1024M -c:v copy -c:a copy /path/1024M.mp4
ffmpeg -i /path/sourch.mp4 -fs 1280M -c:v copy -c:a copy /path/1280M.mp4
ffmpeg -i /path/sourch.mp4 -fs 1536M -c:v copy -c:a copy /path/1536M.mp4
ffmpeg -i /path/sourch.mp4 -fs 1792M -c:v copy -c:a copy /path/1792M.mp4
ffmpeg -i /path/sourch.mp4 -fs 2048M -c:v copy -c:a copy /path/2048M.mp4
測試程式碼
純js測試程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>檔案md5</title>
<script src="./SparkMD5.js"></script>
</head>
<body>
<input id="file" type="file" />
<script>
document.querySelector('#file').addEventListener('change', e => {
let startTime = Date.now()
const file = e.target.files[0];
const fileReader = new FileReader()
console.log('size', file.size / 1024 / 1024 / 1024, "G")
fileReader.onprogress = e => {
console.log(`${Math.floor((e.loaded / e.total) * 100)}%`)
}
let usedTime = 0
const md5 = new SparkMD5();
fileReader.readAsBinaryString(file);
fileReader.onload = e => {
md5.appendBinary(e.target.result);
const md5Str = md5.end()
usedTime += Date.now() - startTime
console.log('usedTime', usedTime, 'ms')
console.log('md5', md5Str)
}
});
</script>
</body>
</html>
wasm(go)原始碼
請參考:
github.com/butoften/wa…
js+wasm測試程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>檔案md5</title>
<script src="./wasm_exec.js"></script>
</head>
<body>
<script>
function handleSayHello(message) {
console.lof('str from go', message)
}
const go = new Go();
WebAssembly.instantiateStreaming(fetch('wasm/md5.wasm'), go.importObject)
.then(res => {
go.run(res.instance);
});
</script>
<input id="file" type="file" />
<script>
document.querySelector('#file').addEventListener('change', e => {
let startTime = Date.now()
const file = e.target.files[0];
const fileReader = new FileReader()
console.log('size', file.size / 1024 / 1024 / 1024, "G")
fileReader.onprogress = e => {
console.log(`${Math.floor((e.loaded / e.total) * 100)}%`)
}
let usedTime = 0
fileReader.readAsArrayBuffer(file);
fileReader.onload = e => {
const bytes = new Uint8Array(e.target.result)
wasmMd5Add(bytes)
const md5Hash = wasmMd5End()
usedTime += Date.now() - startTime
console.log('usedTime', usedTime, 'ms')
console.log('md5', md5Hash)
}
});
</script>
</body>
</html>
測試條件
- 從FileReader開始讀取算起到md5計算結束,因為現實中,我們需要做loading條動畫比例
- mac 2.7 GHz 雙核Intel Core i5
- mac 8 GB 1867 MHz DDR3
測試目標
chrome (版本:103.0.5060.114)
- 2048M 測試5次分別用時:
- 如果分段計算,每段使用512M
序號 | 純js | 純js分段 | js+wasm | js+wasm分段 |
---|
1 | 37477 ms | 25638 ms | 31680 ms | 22898 ms |
2 | 32926 ms | 28088 ms | 32516 ms | 25168 ms |
3 | 33413 ms | 31412 ms | 33424 ms | 20547 ms |
4 | 35054 ms | 35821 ms | 33906 ms | 23130 ms |
5 | 35986 ms | 36895 ms | 29014 ms | 22011 ms |
序號 | 純js | 純js分段 | js+wasm | js+wasm分段 |
---|
1 | 16298 ms | 19441 ms | 27322 ms | 19233 ms |
2 | 11593 ms | 29424 ms | 28955 ms | 18602 ms |
3 | 24589 ms | 28685 ms | 28192 ms | 18472 ms |
4 | 24725 ms | 29892 ms | 28931 ms | 18260 ms |
5 | 24695 ms | 31453 ms | 36166 ms | 19474 ms |
序號 | 純js | 純js分段 | js+wasm | js+wasm分段 |
---|
1 | 19856 ms | 19591 ms | 21259 ms | 15920 ms |
2 | 15119 ms | 26283 ms | 20821 ms | 15634 ms |
3 | 21387 ms | 25861 ms | 22473 ms | 16893 ms |
4 | 19550 ms | 25797 ms | 21793 ms | 17239 ms |
5 | 20363 ms | 26402 ms | 20782 ms | 15786 ms |
序號 | 純js | 純js分段 | js+wasm | js+wasm分段 |
---|
1 | 6449 ms | 12169 ms | 22856 ms | 16621 ms |
2 | 14695 ms | 17558 ms | 19147 ms | 18014 ms |
3 | 17792 ms | 20326 ms | 17203 ms | 14683 ms |
4 | 18094 ms | 16452 ms | 18396 ms | 14399 ms |
5 | 15830 ms | 19006 ms | 19241 ms | 14119 ms |
序號 | 純js | 純js分段 | js+wasm | js+wasm分段 |
---|
1 | 5003 ms | 9441 ms | 16233 ms | 9252 ms |
2 | 6240 ms | 14917 ms | 11145 ms | 9316 ms |
3 | 8563 ms | 10849 ms | 12653 ms | 10963 ms |
4 | 10261 ms | 12155 ms | 11607 ms | 9108 ms |
5 | 8775 ms | 11138 ms | 9869 ms | 10451 ms |
序號 | 純js | 純js分段 | js+wasm | js+wasm分段 |
---|
1 | 4632 ms | 7721 ms | 9590 ms | 7887 ms |
2 | 5858 ms | 3312 ms | 7161 ms | 7963 ms |
3 | 2859 ms | 10808 ms | 7646 ms | 7973 ms |
4 | 3531 ms | 8614 ms | 7904 ms | 8197 ms |
5 | 5744 ms | 7612 ms | 7131 ms | 10714 ms |
序號 | 純js | 純js分段 | js+wasm | js+wasm分段 |
---|
1 | 3329 ms | 5884 ms | 9318 ms | 7270 ms |
2 | 7222 ms | 9917 ms | 6897 ms | 7096 ms |
3 | 2602 ms | 6066 ms | 6295 ms | 6908 ms |
4 | 2757 ms | 6662 ms | 6551 ms | 8164 ms |
5 | 2509 ms | 8730 ms | 7126 ms | 7039 ms |
序號 | 純js | 純js分段 | js+wasm | js+wasm分段 |
---|
1 | 2721 ms | 2824 ms | 6557 ms | 5019 ms |
2 | 3241 ms | 6867 ms | 4943 ms | 5026 ms |
3 | 1803 ms | 3012 ms | 4902 ms | 5052 ms |
4 | 1930 ms | 3010 ms | 5007 ms | 5022 ms |
5 | 1807 ms | 2885 ms | 4881 ms | 5238 ms |
序號 | 純js | js+wasm |
---|
1 | 6406 ms | 3358 ms |
2 | 6435 ms | 3599 ms |
3 | 6450 ms | 3283 ms |
4 | 6286 ms | 3952 ms |
5 | 6408 ms | 3207 ms |
序號 | 純js | js+wasm |
---|
1 | 3497 ms | 1705 ms |
2 | 3412 ms | 1643 ms |
3 | 3263 ms | 1825 ms |
4 | 3284 ms | 1710 ms |
5 | 3376 ms | 1768 ms |
序號 | 純js | js+wasm |
---|
1 | 1873 ms | 923 ms |
2 | 1776 ms | 928 ms |
3 | 1772 ms | 913 ms |
4 | 1682 ms | 923 ms |
5 | 1742 ms | 898 ms |
序號 | 純js | js+wasm |
---|
1 | 1043 ms | 516 ms |
2 | 877 ms | 479 ms |
3 | 907 ms | 504 ms |
4 | 872 ms | 459 ms |
5 | 865 ms | 495 ms |
序號 | 純js | js+wasm |
---|
1 | 487 ms | 209 ms |
2 | 387 ms | 209 ms |
3 | 410 ms | 225 ms |
4 | 512 ms | 268 ms |
5 | 399 ms | 225 ms |
序號 | 純js | js+wasm |
---|
1 | 147 ms | 92 ms |
2 | 133 ms | 90 ms |
3 | 177 ms | 94 ms |
4 | 157 ms | 42 ms |
5 | 175 ms | 84 ms |
序號 | 純js | js+wasm |
---|
1 | 71 ms | 20 ms |
2 | 66 ms | 24 ms |
3 | 45 ms | 33 ms |
4 | 80 ms | 30 ms |
5 | 97 ms | 29 ms |
firefox (版本號:103.0.1 (64 位))
- 2048M 載入到52%時頁面崩潰
- 採用Blob.slice方式分段計算
- 每512M為一段,測試5次
序號 | 純js分段 | js+wasm分段 |
---|
1 | 51398 ms | 17338 ms |
2 | 41282 ms | 16385 ms |
3 | 42358 ms | 16966 ms |
4 | 43363 ms | 15843 ms |
5 | 40802 ms | 16551 ms |
- 1792M 載入到59%時頁面崩潰
- 採用Blob.slice方式分段計算
- 每512M為一段,測試5次
序號 | 純js分段 | js+wasm分段 |
---|
1 | 33690 ms | 13251 ms |
2 | 37423 ms | 13636 ms |
3 | 42903 ms | 13487 ms |
4 | 32684 ms | 13662 ms |
5 | 36691 ms | 14984 ms |
- 1536M 載入到69%時頁面崩潰
- 採用Blob.slice方式分段計算
- 每512M為一段,測試5次
序號 | 純js分段 | js+wasm分段 |
---|
1 | 28051 ms | 11425 ms |
2 | 27822 ms | 11337 ms |
3 | 28331 ms | 12508 ms |
4 | 30089 ms | 11520 ms |
5 | 32890 ms | 11507 ms |
- 1280M 載入到83%時頁面崩潰
- 採用Blob.slice方式分段
- 計算512M為一段
序號 | 純js分段 | js+wasm分段 |
---|
1 | 25680 ms | 9571 ms |
2 | 23956 ms | 9549 ms |
3 | 28829 ms | 10070 ms |
4 | 23518 ms | 9449 ms |
5 | 23200 ms | 9540 ms |
序號 | 純js | js+wasm |
---|
1 | 38277 ms | 7776 ms |
2 | 40936 ms | 11254 ms |
3 | 29861 ms | 7653 ms |
4 | 25630 ms | 7517 ms |
5 | 18934 ms | 11443 ms |
6 | 24849 ms | 8039 ms |
7 | 18214 ms | 7727 ms |
8 | 18617 ms | 12987 ms |
9 | 33281 ms | 7523 ms |
10 | 40757 ms | 8895 ms |
序號 | 純js | js+wasm |
---|
1 | 22752 ms | 8605 ms |
2 | 16669 ms | 9313 ms |
3 | 15716 ms | 6678 ms |
4 | 16940 ms | 6521 ms |
5 | 16732 ms | 9269 ms |
6 | 15805 ms | 6582 ms |
7 | 15718 ms | 6519 ms |
8 | 15795 ms | 9377 ms |
9 | 15641 ms | 6773 ms |
10 | 15622 ms | 7489 ms |
序號 | 純js | js+wasm |
---|
1 | 15181 ms | 8333 ms |
2 | 14031 ms | 5880 ms |
3 | 14214 ms | 5987 ms |
4 | 33812 ms | 5935 ms |
5 | 14167 ms | 8666 ms |
6 | 14666 ms | 8031 ms |
7 | 28640 ms | 5991 ms |
8 | 13992 ms | 5840 ms |
9 | 13926 ms | 6032 ms |
10 | 14216 ms | 6637 ms |
序號 | 純js | js+wasm |
---|
1 | 11418 ms | 4457 ms |
2 | 11199 ms | 5370 ms |
3 | 10717 ms | 4654 ms |
4 | 10607 ms | 4436 ms |
5 | 10611 ms | 4479 ms |
6 | 10718 ms | 4368 ms |
7 | 10560 ms | 5494 ms |
8 | 11519 ms | 5044 ms |
9 | 10802 ms | 4426 ms |
10 | 11779 ms | 4971 ms |
序號 | 純js | js+wasm |
---|
1 | 8362 ms | 2981 ms |
2 | 7516 ms | 2999 ms |
3 | 7335 ms | 3030 ms |
4 | 7357 ms | 3150 ms |
5 | 7444 ms | 3001 ms |
6 | 8456 ms | 3223 ms |
7 | 7376 ms | 3120 ms |
8 | 7313 ms | 3072 ms |
9 | 7349 ms | 3240 ms |
10 | 7447 ms | 3352 ms |
序號 | 純js | js+wasm |
---|
1 | 4066 ms | 1525 ms |
2 | 4440 ms | 1516 ms |
3 | 4223 ms | 1510 ms |
4 | 3916 ms | 1610 ms |
5 | 3917 ms | 1509 ms |
6 | 4028 ms | 1588 ms |
7 | 3964 ms | 1514 ms |
8 | 4037 ms | 1507 ms |
9 | 3957 ms | 1506 ms |
10 | 3987 ms | 1642 ms |
序號 | 純js | js+wasm |
---|
1 | 2280 ms | 761 ms |
2 | 2331 ms | 820 ms |
3 | 2193 ms | 798 ms |
4 | 2242 ms | 777 ms |
5 | 2197 ms | 752 ms |
6 | 2330 ms | 769 ms |
7 | 2236 ms | 758 ms |
8 | 2364 ms | 798 ms |
9 | 2278 ms | 783 ms |
10 | 2384 ms | 785 ms |
序號 | 純js | js+wasm |
---|
1 | 1366 ms | 397 ms |
2 | 1355 ms | 378 ms |
3 | 1445 ms | 460 ms |
4 | 1468 ms | 437 ms |
5 | 1417 ms | 406 ms |
6 | 1525 ms | 478 ms |
7 | 1381 ms | 393 ms |
8 | 1450 ms | 430 ms |
9 | 1417 ms | 428 ms |
10 | 1378 ms | 431 ms |
序號 | 純js | js+wasm |
---|
1 | 921 ms | 168 ms |
2 | 871 ms | 162 ms |
3 | 859 ms | 163 ms |
4 | 864 ms | 162 ms |
5 | 1025 ms | 177 ms |
6 | 910 ms | 158 ms |
7 | 904 ms | 150 ms |
8 | 931 ms | 187 ms |
9 | 1014 ms | 182 ms |
10 | 871 ms | 159 ms |
序號 | 純js | js+wasm |
---|
1 | 127 ms | 48 ms |
2 | 124 ms | 50 ms |
3 | 140 ms | 44 ms |
4 | 129 ms | 47 ms |
5 | 127 ms | 51 ms |
6 | 129 ms | 50 ms |
7 | 126 ms | 46 ms |
8 | 119 ms | 54 ms |
9 | 121 ms | 46 ms |
10 | 118 ms | 50 ms |
序號 | 純js | js+wasm |
---|
1 | 46 ms | 18 ms |
2 | 41 ms | 22 ms |
3 | 43 ms | 13 ms |
4 | 40 ms | 15 ms |
5 | 44 ms | 11 ms |
6 | 47 ms | 15 ms |
7 | 42 ms | 11 ms |
8 | 42 ms | 20 ms |
9 | 45 ms | 13 ms |
10 | 44 ms | 16 ms |
分段計算測試程式碼
純js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>檔案md5</title>
<script src="./SparkMD5.js"></script>
</head>
<body>
<input id="file" type="file" />
<script>
document.querySelector('#file').addEventListener('change', e => {
let startTime = Date.now()
const file = e.target.files[0];
const fileReader = new FileReader()
console.log('size', file.size / 1024 / 1024 / 1024, "G")
fileReader.onprogress = e => {
console.log(`${Math.floor((e.loaded / e.total) * 100)}%`)
}
let usedTime = 0
const md5 = new SparkMD5();
let index = 0
const chunkSize = 512 * 1024 * 1024;//file.size / count
let count = Math.ceil(file.size / chunkSize)
console.log('分幾份', count)
loadSliceFile();
function loadSliceFile() {
const sliceFile = file.slice(index * chunkSize, index * chunkSize + chunkSize)
fileReader.readAsBinaryString(sliceFile);
}
fileReader.onload = e => {
index += 1;
md5.appendBinary(e.target.result);
if (index < count) {
loadSliceFile()
}
else {
const md5Str = md5.end()
usedTime += Date.now() - startTime
console.log('usedTime', usedTime, 'ms')
console.log('md5', md5Str)
}
}
});
</script>
</body>
</html>
js+wasm
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>檔案md5</title>
<script src="./wasm_exec.js"></script>
<!-- <script src="./wasm_exec_tiny.js"></script> -->
</head>
<body>
<script>
function handleSayHello(message) {
console.lof('str from go', message)
}
const go = new Go();
WebAssembly.instantiateStreaming(fetch('wasm/md5.wasm'), go.importObject)
.then(res => {
go.run(res.instance); // 執行 golang裡 main 方法
});
</script>
<input id="file" type="file" />
<script>
document.querySelector('#file').addEventListener('change', e => {
let startTime = Date.now()
const file = e.target.files[0];
const fileReader = new FileReader()
console.log('size', file.size / 1024 / 1024 / 1024, "G")
fileReader.onprogress = e => {
console.log(`${Math.floor((e.loaded / e.total) * 100)}%`)
}
let usedTime = 0
let index = 0
const sliceSize = 512
const chunkSize = sliceSize * 1024 * 1024;//file.size / count
let count = Math.ceil(file.size / chunkSize)
console.log('分幾份', count)
loadSliceFile();
function loadSliceFile() {
const sliceFile = file.slice(index * chunkSize, index * chunkSize + chunkSize)
fileReader.readAsArrayBuffer(sliceFile);
}
fileReader.onload = e => {
index += 1;
const bytes = new Uint8Array(e.target.result)
wasmMd5Add(bytes)
if (index < count) {
loadSliceFile()
}
else {
const md5Hash = wasmMd5End()
usedTime += Date.now() - startTime
console.log('usedTime', usedTime, 'ms')
console.log('md5', md5Hash)
}
}
});
</script>
</body>
</html>
測試結論
firefox
- 超過1G的檔案,直接崩潰,只能通過分段計算最終合併計算
- 從1M到2G,wasm的速度是純js計算的2-3倍
- 20M,wasm是純js的 6倍
chrome
- 0-400M時,wasm是純js的2-3倍
- 600M-1024M時,純js不分段比wasm要快
- 分段js比不分段wasm快一點點
- 分段js比分段wasm慢一點點
- 1280M,差不太多
- 大於1280M,js比wasm分段慢
- 對於js,分段要慢一些
- 對於wasm,分段要快一些
最終結論
- chrome對js的優化,使得在600M-1024M期間的大檔案純js計算md5速度要快於wasm,其他範圍還是wasm效能好一些
- 由於firefox超過1G就崩潰了,所以我們平時寫程式碼時,還是要做分段載入的。
- 業務中,還是可以使用wasm來提升效能的
- 可以針對 chrome與其他瀏覽器來製作不同的方案
- 其實golang 計算md5基本上是js的7-9倍,但js給wasm複製資料的時間佔用了太多,導致wasm被降低了速度,檔案越大,複製時間越長,越慢
wasm 還是可以使用的,眾觀全域性,速度提升2-3倍。chrome可以針對性處理
以上就是wasm+js實現檔案獲取md5範例詳解的詳細內容,更多關於wasm js獲取md5的資料請關注it145.com其它相關文章!