spark-MD5 增量计算切片
npm i spark-md5
https://github.com/satazor/js-spark-md5
-
做切片 blob.slice()
-
使用webworker来做多线程,需要把文件单独放在public下,可供直接访问
-
var worker = new Worker('work.js');
-
使用requestIdleCallback ,deadline.timeRemaining() > 1 是否空闲
-
算MD5 重新计算对象,拆成n个新的对象{ chunk, name, hash}, 分别发起后台请求
-
后台拿到碎片之后,存到文件夹中 名字为
[
hash-1,
hash-2,
hash-3,
...
] -
先执行check请求,拿到已经上传是成功的list , 过滤,如果成功的,就不再上传
-
前台发起合并请求,拿到后缀ext,大小size, hash,
-
后台通过pipe来读取流文件,并合并成一个新文件。
合并前需要根据 123来进行sort重拍
断点续传
- 前台发送一个check 请求,判断是否合并的文件是否存在,如果不存在则表示合并失败了,再判断是否存在文件夹,再查看文件夹里缺哪个块
请求数据参数为 ext,hash - 后台拿到参数后判断,{ext} 是否存在 如果存在,返回uploaded=true
如果不存在,判断文件夹是否存在,不存在 返回 uploaded=false,uploadlist=[]
如果存在,读取文件,需要过滤隐藏文件,name[0]!='.' ,返回uploaded=false,uploadlist
method: {
createFileChunk(file, size=1*1024*1024) {
const chunks = [];
let cur = 0;
while(cur < this.file.size) {
chunks.push({index: cur, file: this.file.slice(cur, cur+size)});
cur+=size
}
return chunks
},
calculateHashIdle () {
const spark = new self.SparkMD5.ArrayBuffer()
let progress = 0
let count = 0
const workLoop = async deadline
},
calculateHashWorker () {
return new Promise(resolve => {
this.worker = new Worker('/hash.js') // 新建一个work线程
this.worker.postMessage({chunks: this.chunks})
this.worker.onmessage = e => {
const {progress, hash = e.data} // 拿到进度和hash
this.hashProgress = Number(propgress.toFixed(2))
if (hash ) { // 当hash 有值,则表示已经有结果,返回这个hash值
resolve(hash)
}
}
})
},
calculateHashIdle () {
const workloop= async deadline => {
while(count<chunks.length && deadline.timeRemaining() > 1) {
// 空闲,且转MD5任务还没执行完
// 执行分片
}
window.requestIdleCallback(workloop)
}
window.requestIdleCallback(workloop)
},
async unloadFile() {
if (!this.isImage(this.file)) {
console.log("不合规则")
return
}
const chunks = this.createFileChunk(this.file)
const hash = await this.calculateHashWorker()
// 进行上传
this.chunks = chunks.map((chunk, index) => {
const form = new FormData()
form.append('chunk', chunk.chunk),
form.append('hash', chunk.hash),
form.append('name', chunk.name + index)
return form
}).map((form, index) => this.$http.post('/uploadfile', {
// 对每个切片进行分开上传
onUploadProgress: progress => {
}
}))
/** 不能再直接拼成formData上传了
const form = new FormDate();
form.append('name', 'file');
form.append('file', this.file);
const ret = await this.$http.post('/upoadfile', form, {
onUploadProgress: progress => {
this.uploadProgress = Number(((progress.loaded / progress.total)*100).toFixed(e))
}
})
*/
}
}
// hash.js
// 引入spark-md5
/**
Worker 线程有一些自己的全局属性和方法。
self.name: Worker 的名字。该属性只读,由构造函数指定。
self.onmessage:指定message事件的监听函数。
self.onmessageerror:指定 messageerror 事件的监听函数。发送的数据无法序列化成字符串时,会触发这个事件。
self.close():关闭 Worker 线程。
self.postMessage():向产生这个 Worker 线程发送消息。
self.importScripts():加载 JS 脚本。
*/
self.importScripts('spak0md5.min.js')
self.onmessage = e => {
const {chunk} = e.data
// self代表子线程自身,即子线程的全局对象。
const spark = new self.SparkMD5.ArrayBuffer()
let progress = 0
let count = 0
const loadNext = index => {
const reader = new FileReader()
reader.readAsArrayBuffer(chunks[index].file) // 会将文件内容读取为ArrayBuffer对象
reader.onload = e => { // 当读取操作成功完成时调用
count ++
spark.append(e.target.result)
if () {
self.postMessage({progress: 100, hash: spak.end()})
} else {
progress += 100/chunks.length
self.postMessage({
progress
})
loadNext (count)
}
}
}
}
网友评论