目录 引言 本文重点 准备工作 测试代码 纯js测试代码 wasm(go)源码 js+wasm测试代码 测试条件 测试目标 chrome (版本:103.0.5060.114) firefox (版本号:103.0.1 (64 位)) 分段计算测试代码
目录
- 引言
- 本文重点
- 准备工作
- 测试代码
- 纯js测试代码
- wasm(go)源码
- js+wasm测试代码
- 测试条件
- 测试目标
- chrome (版本:103.0.5060.114)
- firefox (版本号:103.0.1 (64 位))
- 分段计算测试代码
- 纯js
- js+wasm
- 测试结论
- firefox
- chrome
- 最终结论
引言
在过去的几年里,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
- 1792M 测试5次分别用时:
- 1536M 测试5次分别用时:
- 1280M 测试5次分别用时:
- 1024M 测试5次分别用时:
- 900M 测试5次分别用时:
- 800M 测试5次分别用时:
- 600M 测试5次分别用时:
- 400M 测试5次分别用时:
- 200M 测试5次分别用时:
- 100M 测试5次分别用时:
- 50M 测试5次分别用时:
- 20M 测试5次分别用时:
- 5M 测试10次分别用时:
- 1M 测试5次分别用时:
firefox (版本号:103.0.1 (64 位))
- 2048M 加载到52%时页面崩溃
- 采用Blob.slice方式分段计算
- 每512M为一段,测试5次
- 1792M 加载到59%时页面崩溃
- 采用Blob.slice方式分段计算
- 每512M为一段,测试5次
- 1536M 加载到69%时页面崩溃
- 采用Blob.slice方式分段计算
- 每512M为一段,测试5次
- 1280M 加载到83%时页面崩溃
- 采用Blob.slice方式分段
- 计算512M为一段
- 1024M 测试10次分别用时:
- 900M 测试10次分别用时:
- 800M 测试10次分别用时:
- 600M 测试10次分别用时:
- 400M 测试10次分别用时:
- 200M 测试10次分别用时:
- 100M 测试10次分别用时:
- 50M 测试10次分别用时:
- 20M 测试10次分别用时:
- 5M 测试10次分别用时:
- 1M 测试10次分别用时:
分段计算测试代码
纯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的资料请关注易盾网络其它相关文章!