当前位置 : 主页 > 网络编程 > JavaScript >

wasm+js实现文件获取md5示例详解

来源:互联网 收集:自由互联 发布时间:2023-02-08
目录 引言 本文重点 准备工作 测试代码 纯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
      序号纯js纯js分段js+wasmjs+wasm分段137477 ms25638 ms31680 ms22898 ms232926 ms28088 ms32516 ms25168 ms333413 ms31412 ms33424 ms20547 ms435054 ms35821 ms33906 ms23130 ms535986 ms36895 ms29014 ms22011 ms
      • 1792M 测试5次分别用时:
      序号纯js纯js分段js+wasmjs+wasm分段116298 ms19441 ms27322 ms19233 ms211593 ms29424 ms28955 ms18602 ms324589 ms28685 ms28192 ms18472 ms424725 ms29892 ms28931 ms18260 ms524695 ms31453 ms36166 ms19474 ms
      • 1536M 测试5次分别用时:
      序号纯js纯js分段js+wasmjs+wasm分段119856 ms19591 ms21259 ms15920 ms215119 ms26283 ms20821 ms15634 ms321387 ms25861 ms22473 ms16893 ms419550 ms25797 ms21793 ms17239 ms520363 ms26402 ms20782 ms15786 ms
      • 1280M 测试5次分别用时:
      序号纯js纯js分段js+wasmjs+wasm分段16449 ms12169 ms22856 ms16621 ms214695 ms17558 ms19147 ms18014 ms317792 ms20326 ms17203 ms14683 ms418094 ms16452 ms18396 ms14399 ms515830 ms19006 ms19241 ms14119 ms
      • 1024M 测试5次分别用时:
      序号纯js纯js分段js+wasmjs+wasm分段15003 ms9441 ms16233 ms9252 ms26240 ms14917 ms11145 ms9316 ms38563 ms10849 ms12653 ms10963 ms410261 ms12155 ms11607 ms9108 ms58775 ms11138 ms9869 ms10451 ms
      • 900M 测试5次分别用时:
      序号纯js纯js分段js+wasmjs+wasm分段14632 ms7721 ms9590 ms7887 ms25858 ms3312 ms7161 ms7963 ms32859 ms10808 ms7646 ms7973 ms43531 ms8614 ms7904 ms8197 ms55744 ms7612 ms7131 ms10714 ms
      • 800M 测试5次分别用时:
      序号纯js纯js分段js+wasmjs+wasm分段13329 ms5884 ms9318 ms7270 ms27222 ms9917 ms6897 ms7096 ms32602 ms6066 ms6295 ms6908 ms42757 ms6662 ms6551 ms8164 ms52509 ms8730 ms7126 ms7039 ms
      • 600M 测试5次分别用时:
      序号纯js纯js分段js+wasmjs+wasm分段12721 ms2824 ms6557 ms5019 ms23241 ms6867 ms4943 ms5026 ms31803 ms3012 ms4902 ms5052 ms41930 ms3010 ms5007 ms5022 ms51807 ms2885 ms4881 ms5238 ms
      • 400M 测试5次分别用时:
      序号纯jsjs+wasm16406 ms3358 ms26435 ms3599 ms36450 ms3283 ms46286 ms3952 ms56408 ms3207 ms
      • 200M 测试5次分别用时:
      序号纯jsjs+wasm13497 ms1705 ms23412 ms1643 ms33263 ms1825 ms43284 ms1710 ms53376 ms1768 ms
      • 100M 测试5次分别用时:
      序号纯jsjs+wasm11873 ms923 ms21776 ms928 ms31772 ms913 ms41682 ms923 ms51742 ms898 ms
      • 50M 测试5次分别用时:
      序号纯jsjs+wasm11043 ms516 ms2877 ms479 ms3907 ms504 ms4872 ms459 ms5865 ms495 ms
      • 20M 测试5次分别用时:
      序号纯jsjs+wasm1487 ms209 ms2387 ms209 ms3410 ms225 ms4512 ms268 ms5399 ms225 ms
      • 5M 测试10次分别用时:
      序号纯jsjs+wasm1147 ms92 ms2133 ms90 ms3177 ms94 ms4157 ms42 ms5175 ms84 ms
      • 1M 测试5次分别用时:
      序号纯jsjs+wasm171 ms20 ms266 ms24 ms345 ms33 ms480 ms30 ms597 ms29 ms

      firefox (版本号:103.0.1 (64 位))

      • 2048M 加载到52%时页面崩溃
        • 采用Blob.slice方式分段计算
        • 每512M为一段,测试5次
      序号纯js分段js+wasm分段151398 ms17338 ms241282 ms16385 ms342358 ms16966 ms443363 ms15843 ms540802 ms16551 ms
      • 1792M 加载到59%时页面崩溃
        • 采用Blob.slice方式分段计算
        • 每512M为一段,测试5次
      序号纯js分段js+wasm分段133690 ms13251 ms237423 ms13636 ms342903 ms13487 ms432684 ms13662 ms536691 ms14984 ms
      • 1536M 加载到69%时页面崩溃
        • 采用Blob.slice方式分段计算
        • 每512M为一段,测试5次
      序号纯js分段js+wasm分段128051 ms11425 ms227822 ms11337 ms328331 ms12508 ms430089 ms11520 ms532890 ms11507 ms
      • 1280M 加载到83%时页面崩溃
        • 采用Blob.slice方式分段
        • 计算512M为一段
      序号纯js分段js+wasm分段125680 ms9571 ms223956 ms9549 ms328829 ms10070 ms423518 ms9449 ms523200 ms9540 ms
      • 1024M 测试10次分别用时:
      序号纯jsjs+wasm138277 ms7776 ms240936 ms11254 ms329861 ms7653 ms425630 ms7517 ms518934 ms11443 ms624849 ms8039 ms718214 ms7727 ms818617 ms12987 ms933281 ms7523 ms1040757 ms8895 ms
      • 900M 测试10次分别用时:
      序号纯jsjs+wasm122752 ms8605 ms216669 ms9313 ms315716 ms6678 ms416940 ms6521 ms516732 ms9269 ms615805 ms6582 ms715718 ms6519 ms815795 ms9377 ms915641 ms6773 ms1015622 ms7489 ms
      • 800M 测试10次分别用时:
      序号纯jsjs+wasm115181 ms8333 ms214031 ms5880 ms314214 ms5987 ms433812 ms5935 ms514167 ms8666 ms614666 ms8031 ms728640 ms5991 ms813992 ms5840 ms913926 ms6032 ms1014216 ms6637 ms
      • 600M 测试10次分别用时:
      序号纯jsjs+wasm111418 ms4457 ms211199 ms5370 ms310717 ms4654 ms410607 ms4436 ms510611 ms4479 ms610718 ms4368 ms710560 ms5494 ms811519 ms5044 ms910802 ms4426 ms1011779 ms4971 ms
      • 400M 测试10次分别用时:
      序号纯jsjs+wasm18362 ms2981 ms27516 ms2999 ms37335 ms3030 ms47357 ms3150 ms57444 ms3001 ms68456 ms3223 ms77376 ms3120 ms87313 ms3072 ms97349 ms3240 ms107447 ms3352 ms
      • 200M 测试10次分别用时:
      序号纯jsjs+wasm14066 ms1525 ms24440 ms1516 ms34223 ms1510 ms43916 ms1610 ms53917 ms1509 ms64028 ms1588 ms73964 ms1514 ms84037 ms1507 ms93957 ms1506 ms103987 ms1642 ms
      • 100M 测试10次分别用时:
      序号纯jsjs+wasm12280 ms761 ms22331 ms820 ms32193 ms798 ms42242 ms777 ms52197 ms752 ms62330 ms769 ms72236 ms758 ms82364 ms798 ms92278 ms783 ms102384 ms785 ms
      • 50M 测试10次分别用时:
      序号纯jsjs+wasm11366 ms397 ms21355 ms378 ms31445 ms460 ms41468 ms437 ms51417 ms406 ms61525 ms478 ms71381 ms393 ms81450 ms430 ms91417 ms428 ms101378 ms431 ms
      • 20M 测试10次分别用时:
      序号纯jsjs+wasm1921 ms168 ms2871 ms162 ms3859 ms163 ms4864 ms162 ms51025 ms177 ms6910 ms158 ms7904 ms150 ms8931 ms187 ms91014 ms182 ms10871 ms159 ms
      • 5M 测试10次分别用时:
      序号纯jsjs+wasm1127 ms48 ms2124 ms50 ms3140 ms44 ms4129 ms47 ms5127 ms51 ms6129 ms50 ms7126 ms46 ms8119 ms54 ms9121 ms46 ms10118 ms50 ms
      • 1M 测试10次分别用时:
      序号纯jsjs+wasm146 ms18 ms241 ms22 ms343 ms13 ms440 ms15 ms544 ms11 ms647 ms15 ms742 ms11 ms842 ms20 ms945 ms13 ms1044 ms16 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的资料请关注易盾网络其它相关文章!

      上一篇:vue项目使用cdn加速减少webpack打包体积
      下一篇:没有了
      网友评论