目录
- 引言
- 本文重点
- 准备工作
- 测试代码
- 纯js测试代码
- wasm(go)源码
- js+wasm测试代码
- 测试条件
- 测试目标
- chrome (版本:103.0.5060.114)
- firefox (版本号:103.0.1 (64 位))
- 分段计算测试代码
- 测试结论
- 最终结论
引言
在过去的几年里,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的资料请关注我们其它相关文章!