【前端】大文件切片上传性能优化 使用 web worker 多线程
线程代码
worker.js
//实例化一个 XMLHttpRequest 对象
let xhr = new XMLHttpRequest();
let 延迟关闭线程等待时间毫秒 = 6000
function 取消上传(){
try {
xhr.abort()
}catch (e) {
}
}
addEventListener('message', e => {
const { type, data } = e.data
// console.log('子线程:',data)
switch (type){
case '上传文件':
上传文件(data.file,data.第几个, data.子线程id)
break
case '取消上传':
取消上传()
break
case '安全关闭线程':
取消上传()
setTimeout(self.close, 延迟关闭线程等待时间毫秒)
break
case '关闭线程':
self.close()
break
default:
console.log('未知命令')
break
}
// postMessage('子线程发送的消息')
})
function 上传文件 (file, 第几个, 子线程id){
console.log('子线程id:' + 子线程id + ', 第:' + 第几个 + '片-开始上传')
// 这个要放到调用open函数之前!!!
xhr.upload.onprogress = function (e) {
// e.lengthComputable 是一个布尔值,表示当前上传的资源是否具有可计算的长度
if (e.lengthComputable) {
// 多个文件总共大小,以及已上传大小
// console.log(e.total,e.loaded);
// e.loaded 已传输的字节 e.total 文件传输需要的总字节
var percentComplete = Math.ceil((e.loaded / e.total) * 100); // Math.ceil() 向上取整
// console.log('子线程id:' + 子线程id + ', 第几片:' + 第几个 + '-当前进度:', percentComplete)
}
};
// 文件上传成功
xhr.upload.onload = function () {
// console.log('文件上传完成')
};
// open 必须放 xhr.send() 前面
//设置请求方式及接口地址
xhr.open('POST', 'http://127.0.0.1:8088/robot/flow/upload', true);
// xhr.setRequestHeader('Content-Type', 'application/octet-stream');
xhr.onload = function () {
// self.postMessage({ status: 'chunk_uploaded', message: 'Chunk uploaded successfully' });
};
xhr.onerror = function () {
console.log('上传出错-子线程id:' + 子线程id + ', 第:' + 第几个 + '片')
// self.postMessage({ status: 'error', message: 'Error uploading chunk' });
};
const formData = new FormData();
formData.append('file', file);
// formData.append('currentChunk', currentChunk + 1);
// formData.append('totalChunks', totalChunks);
xhr.onreadystatechange = (e) => { //服务端响应后
/*
0 未打开,xhr.open()方法还未被调用.
1 未发送, xhr.send()方法还未被调用.
2 已获取响应头,xhr.send()方法已经被调用, 响应头和响应状态已经返回.
3 正在下载响应体, xhr.响应体下载中; responseText中已经获取了部分数据.
4 请求完成, 整个请求过程已经完毕.
* */
if (xhr.readyState == 4) { //判断客户端是否可以使用
if(xhr.status == 200){ //表示成功处理请求
// 如果定义为 xhr.responseType='json';,控制台包提示格式错误,建议不设置,使用默认设置
console.log('子线程id:' + 子线程id + ',第几片:' + 第几个 + '-上传完成:', xhr.responseText)
self.postMessage({ type:'上传完成', success: true, message: '上传成功' });
}else{
console.log(xhr.status+' '+xhr.statusText)
}
}
}
// 一般放在最后
xhr.send(formData)
}
export default {}
引用线程 指定线程数
import Worker from "worker-loader!@/workers/worker";
//线程集合
var workers = []
const 线程数 = 5
代码 vue js 都可用
<template>
<div>
<input type="file" id="fileInput" />
<a-button @click="点击上传按钮" type="primary">上传</a-button>
<a-button @click="取消上传" type="primary">取消上传</a-button>
<!-- 进度条-->
<progress id="progressBar" max="100" value="0"></progress>
<div id="status"></div>
</div>
</template>
<script>
import Worker from "worker-loader!@/workers/worker";
//线程集合
var workers = []
const 线程数 = 5
//文件
var file;
export default {
components: {
},
data() {
let that = this
return {
当前第几片 : 0,
总片数 : 0,
每片大小: 1024 * 1024 * 99, // 99 MB chunks (adjust as needed)
}
},
setup(){
// console.log(this.$route.currentRoute.value.query)
// const StrUtil = this.$WTool.StrUtil
},
created() {
let that = this
},
mounted() {
},
methods: {
初始化线程(){
let that = this
//关闭之前的线程
for (let i = 0; i < workers.length; i++) {
workers[i].postMessage({type:'安全关闭线程'})
}
//清空线程
workers = []
//初始化线程
for (let i = 0; i < 线程数; i++) {
workers.push(new Worker())
console.log('初始化线程:' + (i+1))
}
//初始化线程监听回调
for (let i = 0; i < workers.length; i++) {
let worker = workers[i]
worker.onmessage = e => {
const { type, success } = e.data
switch (type){
case '上传完成':
// console.log('主线程:上传完成,继续调用上传方法')
that.开始上传文件(i)
break
default:
console.log('未知命令', type)
break
}
}
console.log('初始化线程onmessage:' + (i+1))
}
},
取消上传(){
console.log('取消上传开始')
for (let i = 0; i < workers.length; i++) {
workers[i].postMessage({type:'安全关闭线程'})
}
console.log('取消上传结束')
},
点击上传按钮(){
//获取文件
file = document.getElementById('fileInput').files[0]
//切片
this.总片数 = Math.ceil(file.size / this.每片大小);
console.log('分片数量:' + this.总片数)
this.初始化线程()
//开始上传
for (let i = 0; i < workers.length; i++) {
this.开始上传文件(i);
}
},
开始上传文件(子线程id){
let that = this
//获取线程
let worker = workers[子线程id]
if(that.当前第几片 >= that.总片数){
console.log('第几个线程:' + 子线程id + ', 上传结束')
return
}
const start = that.当前第几片 * this.每片大小;
const end = Math.min(file.size, start + this.每片大小);
//文件切片
const chunk = file.slice(start, end);
if(chunk){
// console.log("子线程id:" + 第几个线程 + '-线程,让子线程上传文件,当前第几片:', that.当前第几片)
worker.postMessage({type:'上传文件', data:{file:chunk, 第几个:that.当前第几片, 子线程id:子线程id}})
that.当前第几片++
}
}
}
}
</script>
<style>
</style>