引入: hash的计算属于CPU密集型,会阻塞主线程。如果大文件主线程更是会阻塞很久,所以采用webWorker计算hash,把计算结果输出即可。
主线程:
const input = document.querySelector('input');
const chunkSize = 1024 ** 2 * 10 // 设置每个chunk为2M
input.addEventListener('change', async function () {
const file = this.files[0];
// 计算chunks 数量
const chunks = Math.ceil(file.size / chunkSize)
// worker最大创建数量
const maxWorkers = navigator.hardwareConcurrency || 4
// 每个worker需分配chunk量
const workerChunks = Math.ceil(chunks / maxWorkers)
let result = []
let count = 0
for(let i = 0; i < maxWorkers; i++) {
//创建worker
const worker = new Worker('./worker.js', {
type: 'module'
});
//当前worker需要处理chunks的起始index
const start = i * workerChunks
//当前worker需要处理chunks结束的index
const end = Math.min((i + 1) * workerChunks, chunks)
//将尽可能的工作都交给worker工作
worker.postMessage({index: i, file, start, end, chunkSize})
worker.addEventListener('message', (e) => {
//合并worker工作结果
result = [...result, ...(e.data)]
count++
if(count >= maxWorkers) {
//全部worker工作结束后,对结果排序
result = result.sort((a, b) => a.index - b.index)
// 获取结果
console.log(result)
}
})
}
}
worker.js
//使用SparkMD5计算文件hash https://cdnjs.cloudflare.com/ajax/libs/spark-md5/3.0.2/spark-md5.min.js
import './md5.min.js'
self.addEventListener('message', async (e) => {
const { file, start, end, chunkSize, index } = e.data
console.log(`worker${index}开始工作`)
const result = []
for (let i = start; i < end; i++) {
//获取当前chunk的blob起始的位置
const blobStart = i * chunkSize;
//获取当前chunk的blob结束的位置
const blobEnd = Math.min(file.size, (i + 1) * chunkSize)
//从原始文件中截取出 chunk
const chunkFile = file.slice(blobStart, blobEnd)
//计算hash
const chunkHash = await getFileChunkHash(chunkFile)
result.push({
index: i,
start: blobStart,
end: blobEnd,
chunkFile,
chunkHash
})
}
self.postMessage(result)
})
//计算hash函数
function getFileChunkHash (file) {
return new Promise((reslove, reject) => {
const spark = new SparkMD5.ArrayBuffer()
const fileReader = new FileReader()
fileReader.onload = (e) => {
const arrayBuffer = e.target.result
spark.append(arrayBuffer)
reslove(spark.end())
}
fileReader.readAsArrayBuffer(file)
})
}