目录 引言 本文重点 准备工作 测试代码 纯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的资料请关注易盾网络其它相关文章!
