【前端】大文件切片上传性能优化 使用 web worker 多线程

线程代码

worker.js

javascript 复制代码
//实例化一个 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 {}

引用线程 指定线程数

javascript 复制代码
import Worker from "worker-loader!@/workers/worker";
//线程集合
var workers = []
const 线程数 = 5

代码 vue js 都可用

javascript 复制代码
<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>
相关推荐
前端小魔女6 分钟前
Rust赋能前端: 纯血前端将 Table 导出 Excel
前端
mubeibeinv11 分钟前
分页/列表分页
java·前端·javascript
林太白16 分钟前
js属性-IntersectionObserver
前端·javascript
爱吃羊的老虎24 分钟前
【WEB开发.js】getElementById :通过元素id属性获取HTML元素
前端·javascript·html
Thomas_YXQ34 分钟前
Unity3D Lua如何支持面向对象详解
开发语言·游戏·junit·性能优化·lua·unity3d
妙哉73640 分钟前
零基础学安全--HTML
前端·安全·html
咔叽布吉1 小时前
【前端学习笔记】AJAX、axios、fetch、跨域
前端·笔记·学习
GISer_Jing1 小时前
Vue3常见Composition API详解(适用Vue2学习进入Vue3学习)
前端·javascript·vue.js
Dragon Wu1 小时前
TailwindCss 总结
前端·css·前端框架
bpmf_fff1 小时前
十、事件类型(鼠标事件、焦点.. 、键盘.. 、文本.. 、滚动..)、事件对象、事件流(事件捕获、事件冒泡、阻止冒泡和默认行为、事件委托)
前端·javascript