大文件Md5实现以及优化
前言
之前在自己项目中做了一个大文件分片上传的功能,满心欢喜的写道自己的简历中。自己当时只实现了:
- 大文件通过
File.prototype.slice
做分片,循环调用接口上传 - 断点续传也只做到了接口报错,重新调用接口续传
- 大文件MD5只实现到了直接调用一个库
browser-md5-file.js
后面出去面试,面试官提的几个问题都无法回答
- 大文件MD5,页面假死情况怎么处理
- 后台传输怎么处理
后续自己通过查阅整理,通过如下方案实现了大文件MD5的功能。
如文章有错误的地方,感谢您的指正
也欢迎您分享更好的实现方式
介绍
主要使用worker线程进行大文件md5加密的优化
实现方式
直接引入 vue / element-ui 源码,与相关md5的库
项目启动方式
- 全局安装一个 http-server
npm install --global http-server
- 在根文件夹下,启动一个服务器
http-server
源码
主要目录
介绍几个主要目录下的的内部功能介绍:
-
md5-test
对于文件 File / Blob 类型的数据进行不同md5库的加密方式进行验证,
我这里使用Hash_1.0.4.exe
程序获取一张图片的md5值为93d16b5ee12efa6af044b5ccaf02cec6
现验证主要包括以下方式:文件读取方式 md5库 md5库算法 计算结果 是否正确 readAsBinaryString spark-md5 SparkMD5.hash 419fd4afc295e632efbce61ca49c8f7e false readAsBinaryString spark-md5 SparkMD5.append & SparkMD5.end AÔ¯Âæ2ï¼æ¤~ false readAsArrayBuffer spark-md5 SparkMD5.hash 93d16b5ee12efa6af044b5ccaf02cec6 true readAsArrayBuffer spark-md5 SparkMD5.ArrayBuffer & append & end 93d16b5ee12efa6af044b5ccaf02cec6 true readAsDataURL spark-md5 SparkMD5.hash cf4f4de6ada92285b12ef1094a70af81 false readAsArrayBuffer md5-js md5 93d16b5ee12efa6af044b5ccaf02cec6 true readAsBinaryString md5-js md5 419fd4afc295e632efbce61ca49c8f7e false 结论:
- 无论是使用
spark-md5
还是使用md5-js
库的时候,只要是读取方式是采用readAsArrayBuffer
是正确的 spark-md5
无论是直接使用hash
方式 还是使用append
拼接方式的时候都是能够正常计算出来
- 无论是使用
-
web-workers
大文件下的md5计算的方式(请优先准备一个超过2GB的文件):-
index.js
testFileReaderLimitAndSingleThread
该方法测试项目:- 当读取的文件大小
size >= 2 GB
的时候,FileReader
直接监听到了error
方法
该方法只是用过chrome浏览器实验得出来的,经过查阅并没有找到对于
FileReader
的大小限制的说明。所以暂不明确这是否是浏览器的标准或者不同浏览器下有不同的限制 - 测试js单线程,并且事件是以队列形式进行的,具体现象如下:
- 在计算文件md5的过程中,点击页面内的 【显示弹框】 按钮的时候,页面不会展示出来
this.$message.success(`点击了${++this.clickCount}次`)
的弹框
此时由于计算md5速率比较慢,并且由于
js
是单线程,所以会导致”页面假死”的现象- 如果多次点击的情况下计算完成后,会【一次性】全部弹出刚才的弹框,并且按照点击顺序展示
展示出来
js
的事件队列的形式 - 在计算文件md5的过程中,点击页面内的 【显示弹框】 按钮的时候,页面不会展示出来
- 当读取的文件大小
testFileReaderWorker
该方法的测试项目为:- 分片的基础方法:
slice
- 开启
web worker
线程,具体现象如下:- 后台计算md5,点击页面内的 【显示弹框】 按钮的时候,页面会展示出来
this.$message.success(`点击了${++this.clickCount}次`)
的弹框
此时由于计算md5速率比较慢的计算放到
worker
的线程中,所以页面内的线程不会被阻塞 - 后台计算md5,点击页面内的 【显示弹框】 按钮的时候,页面会展示出来
- 分片的基础方法:
testFileMultipleUpload
该方法的测试项目为:- 将超过2GB的文件计算md5的完整计算放到
worker
线程中
- 将超过2GB的文件计算md5的完整计算放到
-
worker.js
web worker
的几个说明:web worker
引入文件使用importScripts
这里的路径是使用的相对路径- 全局对象使用
self
来获取 - 使用
web wokder
线程注意以下几个注意点- 同源限制
分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源。 - DOM 限制
Worker 线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的 DOM 对象,也无法使用document、window、parent这些对象。但是,Worker 线程可以navigator对象和location对象。 - 通信联系
Worker 线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息完成。 - 脚本限制
Worker 线程不能执行alert()方法和confirm()方法,但可以使用 XMLHttpRequest 对象发出 AJAX 请求。 - 文件限制
Worker 线程无法读取本地文件,即不能打开本机的文件系统(file://),它所加载的脚本,必须来自网络。
- 同源限制
multipleHash
计算的主要思路:- 使用
file.slice
方法获取切片Blob
- 将切片
Blob
使用FileReader.prototype.readAsArrayBuffer
来获取到切片的ArrayBuffer
- 将切片后的
ArrayBuffer
使用SparkMD5.ArrayBuffer & append & end
方式来计算md5
- 计算完成后将
md5
值通过self.postMessage(md5)
的方式传递给主线程
过程中发现的问题:
- 直接
while
循环将file.slice(startIdx, endIdx)
的结果读取后使用readAsArrayBuffer
完成后通过load
的回调来完成。由于是异步的,所以无法保证SparkMD5.ArrayBuffer.prototype.append
是按照正确的顺序来完成插入 SparkMD5.ArrayBuffer.prototype.append
是异步的所以调用SparkMD5.ArrayBuffer.prototype.end
的时间节点无法获取到, 导致self.postMessage(md5)
的方式传递给主线程
的时间节点无法确定
解决方案及代码实现:
- 实现一个
Md5Queue
队列来收集切片(Md5Queue.prototype.push
) - 实现一个
Md5Queue.prototype.calcMd5
来递归调用SparkMD5.ArrayBuffer.prototype.append
最后切片取完之后调用SparkMD5.ArrayBuffer.prototype.end
,获取到md5
值 - 使用一个订阅-发布模式
evnnt
来做通知主线程
md5值计算完成
- 使用
-
utils/base.js
Md5Queue
使用队列的方式来完成大文件分片方式计算md5Event
模仿使用vue的事件中心源码来实现订阅-发布者模式FileReaderSecondry
二次封装出来一个FileReader
的类TimeTemp
封装出来一个记录时间戳的单例,方便展示md5整个过程的时长
-
几个疑惑等待后续调研
- 现在使用的是直接引入html/js/css的方式来做代码实现,并没有使用
vue-cli
的形式来做。 那么在使用vue-cli
的形式下,web worker
的注册以及注册后的postMessage
要怎么通知? - 如果使用
vue-cli
的形势下,到了具体页面每次都需要重新注册一次web worker
的线程吗? - 如果一个浏览器有多个相同的标签页,这个标签页都有注册一次
web worker
的代码,那么他是只注册一次,还是注册多次?
注册多次那么由于核心数的限制,会不会出问题? navigator.hardwareConcurrency
可以获取到当前计算机的cpu核心数, 那么Worker
类可以拿到当前已开启的线程数量吗?可以通过这个来做开启线程的判断
参考文章:
segmentfault.com/a/119000001…
segmentfault.com/a/119000001…
juejin.cn/post/684490…
今天的文章大文件Md5实现以及优化分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/20303.html