js-sha3计算大文件的SHA3 Hash

1 篇文章 0 订阅

先上代码

import { sha3_256 } from 'js-sha3'

fileChange (e) {
      let file = e.target.files[0];
      let reader = new FileReader();
      reader.readAsArrayBuffer(file);
      reader.onload = function(ev) {
        try {
          let str = sha3_256(ev.target.result);
          console.log(str);
        } catch (e) {
          console.log(e);
        }
      }
    }

认真点哦~

浏览器端的 FileReader 对象提供了 readAsArrayBuffer 的方法,可以将文件的二进制内容读取到ArrayBuffer字节数组对象中,然后就能通过JS去操作包含文件内容的字节数组,这也让浏览器端实现文件哈希提供了可能

File 对象本身只代表文件本身和其中部分元数据,对于文件的内容,浏览器 JS 是通过FileReader等对象来操作(读取)的。FileReader的用法也十分简单,需要注意的是,它是异步的API,所以需要绑定一下回调函数,然后调用 readAsArrayBuffer 让浏览器发起文件读取请求:

let reader = new FileReader(); 			
reader.onloadend =function () {
   console.log(reader.result); 			
} 			
reader.readAsArrayBuffer(selectedFile);

接下来浏览器将会通过系统调用把 selectedFile 变量所指代的 File 对象的内容读取到内存中。这里存在一个问题,载入文件的时候,JS引擎需要向内存申请一块与文件内容等大的内存空间来存放这个文件的内容,显然,在内存有限的前提下,直接读取的做法是处理不了太大的文件的。从哈希算法角度来说,哈希的过程,实际上也是把原文加上 padding 之后以一个个分组为单位来进行的,也就意味着,我们可以在输出最终结果之前,分批读取原文,输入哈希函数,最后从哈希函数的最终状态中读取结果。

只要我们在浏览器端实现文件的部分读取的话,这一套流程就能打通了,需求也得以实现。正好,在浏览器的 JS 环境中,File 对象的原型是名为 Blob 的对象,Blob 的定义是一段不可变的原始二进制数据,在浏览器JS的环境中,文件被抽象成了 Blob 所描述的一块只读的二进制数据。在 Blob 对象中,实现了一个 Blob.slice([Range]) 方法,执行这个方法返回的是一个新的 Blob 对象,这个新的 Blob 所代表的是方法执行时通过 Range 参数指定的部分,类似“分割”的感觉。File 对象继承了 Blob 的所有方法,所以同样地,我们也可以通过 File.slice() 方法实现返回一个只代表其中一部分内容的新的 File 对象。接下来,我们再使用 FileReader 来读取这个新的 File 对象,就能让浏览器底层通过系统调用读取相应 Range 的字节载入到内存中了。

这里又衍生了一个问题,JavaScript 的语句是单线程运行的,在 JavaScript 对一个 chunk 进行运算的时候,浏览器是做不了 UI 重排重绘等一系列事情的,若这个过程的时间消耗太久,超过了肉眼对刷新的感知的话,页面会给人一种卡顿感。虽然说这个小 Demo 不怎么需要关注 UI 的性能问题,假如这个技术应用到了实际的网站呢?(比如说网盘网站的秒传功能的实现)

这里有两种选择,一种是适当地调整 Chunk 的大小,使其计算的过程耗时不超过肉眼能感知的刷新时间;另一种是,利用 Web Worker 新建一个JS线程,把计算哈希的任务交给它,然后只要等待结果就好了。第一种方法很好实现,把 chunk 的大小设为 1M,基本没有什么问题。但,毕竟第二种方法是新东西,还是要尝试一下的

这里需要重点解决的是主线程与 Worker 的通信问题,浏览器主线程提供了 worker.postMessage 方法与 window.onmessage 事件来实现通信。起初还有点担心把 Chunk 传入 Worker 会有内存拷贝的多余开销,后来发现浏览器 API 提供的 Transferable 对象的设定,打消了这一份顾虑,向 Worker 线程传递文件,理论上是可行的。
如果不想把 Worker 的文件独立开,可以把 Worker 的代码内联在了一个 script 元素,通过创建 Blob 对象,然后 createObjectURL 的方式来运行这个 Worker

<script id="worker" type="javascript/worker">

let hasher;
self.addEventListener('message', function (e) {
  let msg = e.data;
  switch (msg.type) {
    case 'url':
      let { url } = msg;
      url = url.substring(0, url.lastIndexOf('/') + 1);
      importScripts(url + 'sha3.js');
      // init
      hasher = sha3_256.create();
      break;
    case 'update':
      hasher.update(msg.input);
      self.postMessage({
        type: 'success'
      });
      break;
    case 'result':
      self.postMessage({
        type: 'result',
        result: hasher.hex(),
      });
      self.close();
      break;
    default:
      break;
  }
}, false);
</script>

大概主要内容就是这些了,最后附上原文链接,好好熟读并背诵下来哦

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值