1、概述
本文将阐述 为何需要计算文件的MD5值,为何需要在web-worker中计算,具体的实现方案与对比
2、为何需要计算文件的MD5值
文件的MD5值是文件的哈希映射,可以简单理解为基于文件内容生成的唯一码,在文件上传中,可以通过在文件头写入MD5值在服务端和客户端之间进行约定,避免接收到的是损坏或者篡改的内容
3、为何需要在web worker中计算
文件的MD5值计算是纯运算,将阻塞JS主线程的运行,在文件较大时,计算过程将使得浏览器长时间处于无响应状态
而web-worker与JS处于不同的线程,相互并不阻塞,因此可以将一些耗时的纯运算放在web-worker中进行,等待完成后通知到JS主线程
看一个简单的例子:
<input type="file" id="file"/> <script src="https://cdn.bootcss.com/crypto-js/3.1.9-1/crypto-js.min.js"></script> file.onchange = ((e)=>{ var reader = new FileReader(); reader.onload = function (e) { // CryptoJS.MD5 参数必须是 string 或者 ArrayBuffer console.log(CryptoJS.MD5(reader.result).toString()) }; reader.readAsText(e.target.files[0], "utf-8"); }) setInterval(() => { console.log(1) }, 1000)setInterval 的执行在计算过程中会被中断
4、web-worker 实现方案
要点:
(1) MD5 的计算不自己造轮子,采用 CryptoJS 提供的方法,需要注意的是 CryptoJS.MD5 接收的参数是 String 类型,因此需要使用 FileReader.readAsText 先将文件转成 String
(2) Worker 不能读取本地文件,因此测试时需要启动一个http的本地服务
(3) 主线程通过 worker.postMessage 向 Worker线程 发送文件内容,onmessage监听Worker线程返回的数据,Worker线程使用 addEventListener 监听 message 事件,在收到数据并完成计算后使用 self.postMessage 返回给主线程,其中 self 的Worker线程的全局对象
(4) 主线程引入的三方库不能共享给 Worker线程,因此需要在 Worker线程 中使用 importScripts 动态加载
主线程代码,相较原始方案,此例把文件的解析和MD5的计算放到了Worker线程("./fileworker.js")中,并且删除了 CryptoJS 的CDN,因为用不到了
<input type="file" id="file"/> var worker = new Worker("./fileworker.js") worker.onmessage = function(e){ console.log(e.data) // 得到计算后的md5 } file.onchange = ((e)=>{ worker.postMessage(e.target.files[0]) }) setInterval(() => { console.log(1) }, 1000)fileworker.js 代码
// 动态加载依赖 self.importScripts("https://cdn.bootcss.com/crypto-js/3.1.9-1/crypto-js.min.js") self.addEventListener("message", (e) => { readFile(e.data) }) function readFile(file) { var reader = new FileReader(); reader.onload = function (e) { // CryptoJS.MD5 参数必须是 string self.postMessage(CryptoJS.MD5(reader.result).toString()) }; reader.readAsText(file, "utf-8"); }计算过程中不会影响 setInterval 的执行
5、后续的实践
上文实现的是一个基础的计算文件MD5的功能,但在工程化的项目中,需要考虑webpack的配置,尝试使用模块的方式注入三方库而不是依赖CDN等等,后续补充。