大文件上传,前端vue 做分片上传

html – 以弹窗的形式



  
上传压缩包 上传压缩包
1、请上传文件类型为zip的算法文件; 2、上传可能需要一些时间,上传过程中建议不要关闭弹窗。
{{ uploadAllData.name }} {{ uploadAllData.type == 1? '算法文件': '测试集文件' }} {{ uploadAllData.size }} {{ uploadAllData.isSuccess?uploadAllData.msg: uploadAllData.isStop? '暂停中': uploadAllData.percent+'%' + ' ' + uploadAllData.speed + '/s' }} {{ uploadAllData.isStop?'▶': '‖' }}

js – 初始化数据

// 初始化数据
initUpload() {
  this.fileData = null;
  this.chunks = [];
  this.isShowUpload = false;
  this.uploadAllData = {
    id: null,
    isSuccess: false,
    isStop: false,
    msg: '',
    name: '',
    size: '0',
    speed: "0",
    percent: 0,
    type: 1,
  };
  this.progressBarWidth = '0%';
  this.fileData = null;
  this.chunkSize = 0;
  this.fileSize = 0;
  this.isShowUploadBtn = true;
  this.uploadedChunks = 0;
},

js – 上传前判断文件类型

// 上传前判断
checkFileType(file) {
  // 先初始化,预防在上传过程中继续点击上传
  this.initUpload();
  // 禁止继续点击上传
  this.isShowUploadBtn = false;
  // 判断只能上传.zip压缩包
  const allowedExtensions = ['zip']; // 允许的文件后缀名列表
  const extension = file.name.split('.').pop().toLowerCase(); // 获取文件的后缀名
  if (allowedExtensions.indexOf(extension) === -1) {
    this.$message.error('只允许上传zip格式的文件');
    this.isShowUploadBtn = true;
    return false; // 阻止文件上传
  }
  return true; // 允许文件上传
},

js – 上传前准备、获取传输任务(旧)
!说明: 因为后台需要文件的md5校验文件的完整性,所以需要读取文件,但是文件太大的时候,导致前端读取的时候就需要很长时间了,经协调,去掉参数md5

import SparkMD5 from 'spark-md5';
// 上传算法文件
requestFile(file) {
  this.fileData = file.file;
   const reader = new FileReader();
   console.log('开始分片任务!');
   reader.onload = (event) => {
     const fileObj = event.target.result;
     const spark = new SparkMD5.ArrayBuffer(); // 获取文件的md5 哈希值 -- 后台用于验证文件的完整性
     console.log(spark, 'md5');
     spark.append(fileObj);
     let md5Hash = spark.end();
     console.log(md5Hash, 'md5');
     spark.destroy(); //释放缓存
     // 将文件分片
     this.chunkSize = 1024 * 1024 * 20 // 每个分片的大小20M
     this.fileSize = this.fileData.size; // 文件总大小
     this.chunkCount = Math.ceil(this.fileSize / this.chunkSize) // 分片数量
     this.uploadedChunks = 0 // 重置已上传的分片数量
     this.startTime = new Date().getTime() // 记录上传开始时间
     // 逐个上传分片
     for (let index = 0; index < this.chunkCount; index++) {
       const start = index * this.chunkSize
       const end = Math.min(start + this.chunkSize, this.fileSize)
       const chunk = this.fileData.slice(start, end)
       this.chunks.push(chunk)
     }
     console.log(this.chunks);
     console.log(this.fileData);
    // 整合上传前的参数,请求获取id
     let beforeUpload = {
       belongsId: this.algorithmVersionId,
       chunkNumberCount: this.chunkCount,
       fileSavePath: "",
       fileType: 1, // 1为算法文件; 2为测试集
       md5: md5Hash,
       pauseOffset: 0,
       status: 1,
       transmitType: 1,
       uploaded: this.uploadedChunks,
       fileName: this.fileData.name
     }
     console.log(beforeUpload, '参数整合');
     httpPost('/api/v1/acctm_file_transmit_task', beforeUpload) // 文件分片传输任务表
       .then(res => {
        if(res.code == '10000') {
           this.currentUploadId = res.data;
           let num = (this.fileSize/(1024*1024)).toFixed(2); // B => MB
           let fileSizeStr = "";
           if(num >= 1024) {
             fileSizeStr = num + 'GB'
           } else if(1 <= num < 1024 ) {
             fileSizeStr = num + 'MB'
           } else {
             fileSizeStr = this.fileSize + 'KB'
           }
           this.uploadChunk(this.chunks, 0, this.chunkSize, this.fileData.name, fileSizeStr)
         } else {
           this.isShowUploadBtn = true;
           this.$message({
             type: 'error',
             message: '文件分片传输失败!'
           })
         }
       })
       .catch(() => {
         console.log('到这人了?');
       })
   };
  reader.readAsArrayBuffer(this.fileData);
  将文件分片
  this.chunkSize = 1024 * 1024 * 20 // 每个分片的大小20M
  this.fileSize = this.fileData.size; // 文件总大小
  this.chunkCount = Math.ceil(this.fileSize / this.chunkSize) // 分片数量
  this.uploadedChunks = 0 // 重置已上传的分片数量
  this.startTime = new Date().getTime() // 记录上传开始时间
  // 逐个上传分片
  for (let index = 0; index < this.chunkCount; index++) {
    const start = index * this.chunkSize
    const end = Math.min(start + this.chunkSize, this.fileSize)
    const chunk = this.fileData.slice(start, end)
    this.chunks.push(chunk)
  }
  let beforeUpload = {
    belongsId: this.algorithmVersionId,
    chunkNumberCount: this.chunkCount,
    fileSavePath: "",
    fileType: 1, // 1为算法文件; 2为测试集
    pauseOffset: 0,
    status: 1,
    transmitType: 1,
    uploaded: this.uploadedChunks,
    fileName: this.fileData.name
  }
  httpPost('/api/v1/acctm_file_transmit_task', beforeUpload) // 文件分片传输任务表
    .then(res => {
      if(res && res.code == '10000') {
        this.currentUploadId = res.data;
        let num = (this.fileSize/(1024*1024)).toFixed(2); // B => MB
        let fileSizeStr = "";
        if(num >= 1024) {
          fileSizeStr = (num/1024).toFixed(2) + 'GB'
        } else if(1 <= num && num < 1024 ) {
          fileSizeStr = num + 'MB'
        } else {
          fileSizeStr = (num*1024).toFixed(2) + 'KB'
        }
        this.uploadChunk(this.chunks, 0, this.chunkSize, this.fileData.name, fileSizeStr)
      } else {
        this.isShowUploadBtn = true;
      }
    })
    .catch(() => {
      console.log('开始分片任务失败!');
    })
},

js – 上传前准备、获取传输任务(目前采用)

// 上传算法文件
requestFile(file) {
  this.fileData = file.file;
  // 将文件分片
  this.chunkSize = 1024 * 1024 * 20 // 每个分片的大小20M
  this.fileSize = this.fileData.size; // 文件总大小
  this.chunkCount = Math.ceil(this.fileSize / this.chunkSize) // 分片数量
  this.uploadedChunks = 0 // 重置已上传的分片数量
  this.startTime = new Date().getTime() // 记录上传开始时间
  // 逐个上传分片
  for (let index = 0; index < this.chunkCount; index++) {
    const start = index * this.chunkSize
    const end = Math.min(start + this.chunkSize, this.fileSize)
    const chunk = this.fileData.slice(start, end)
    this.chunks.push(chunk)
  }
  let beforeUpload = {
    belongsId: this.activeRouter.id,
    chunkNumberCount: this.chunkCount,
    fileSavePath: "",
    fileType: 2, // 1为算法文件; 2为测试集
    pauseOffset: 0,
    status: 1,
    transmitType: 1,
    uploaded: this.uploadedChunks,
    fileName: this.fileData.name
  }
  httpPost('/api/v1/acctm_file_transmit_task', beforeUpload) // 文件分片传输任务表
    .then(res => {
      if(res && res.code == '10000') {
        this.currentUploadId = res.data;
        let num = (this.fileSize/(1024*1024)).toFixed(2); // B => MB
        let fileSizeStr = "";
        if(num >= 1024) {
          fileSizeStr = (num/1024).toFixed(2) + 'GB'
        } else if(1 <= num && num < 1024 ) {
          fileSizeStr = num + 'MB'
        } else {
          fileSizeStr = (num*1024).toFixed(2) + 'KB'
        }
        this.uploadChunk(this.chunks, 0, this.chunkSize, this.fileData.name, fileSizeStr)
      } else {
        this.isShowUploadBtn = true;
      }
    })
    .catch(() => {
      console.log('开始分片任务失败!');
    })
},

js – 开始分片上传
!注意:自己封装的axios 请求触发不了onUploadProgress事件

//  上传分片
uploadChunk(chunks, index, chunkSize, name, fileSize) {
  console.log(chunks, index);
  this.isShowUpload = true;
  this.isShowUploadBtn = true; // 显示加载文件后放开上传的按钮
  this.progressBar = document.getElementById('progress-bar');
  if (index >= chunks.length) {
    // 全部分片上传完成
    this.uploadAllData = {
      id: this.currentUploadId,
      isStop: false,
      isSuccess: true,
      msg: '上传完成',
      name: name,
      size: fileSize,
      speed: '0',
      percent: 0,
      type: 1
    }
    // 重置数据
    this.chunks = [];
    this.progressBarWidth = '0%';
    this.fileData = null;
    this.chunkSize = 0;
    this.fileSize = 0;
    this.uploadedChunks = 0;
    return
  }
  let chunk = chunks[index]; // 当前文件的分片数据
  // 整合参数
  const formData = new FormData()
  formData.append('chunkSize', chunkSize)
  formData.append('file', chunk, index+1) // 文件的分片,从1开始 -- 与后台协商,文件的命名
  formData.append('chunkNumber', index+1); // 分片序号,从1开始 -- 与后台协商
  formData.append('chunkNumberCount', this.chunkCount)
  formData.append('transmitTaskId', this.currentUploadId)
  // 发送分片请求
  let url = process.env.VUE_APP_BASE_API;
  axios.post(url + '/api/v1/acctm_file_transmit_task/chunkUpload', formData, { // 文件分片信息表
      headers: {
        'Content-Type': 'multipart/form-data',
        'Authorization': sessionStorage.getItem('token') || ""
      },
      onUploadProgress: (progressEvent) => {
        const uploaded = progressEvent.loaded
        // const total = progressEvent.total
        // 更新上传进度
        this.progress = Math.round(((index + 1) / this.chunkCount) * 100)
        // this.progress = Math.round((uploaded / total) * 100)
        // 计算上传速度
        const currentTime = new Date().getTime()
        const elapsedTime = (currentTime - this.startTime) / 1000 // 经过的时间(秒)
        this.speed = Math.round((uploaded - this.uploadedSize) / elapsedTime / 1024) // 上传速度(KB/s)
        // console.log(this.speed);
        // 更新已上传大小
        this.uploadedSize = uploaded;
        // 转换单位,超过1M,显示M,超过G,显示G
        let number = (this.speed/1024).toFixed(2); // KB => MB
        // console.log(1 <= number < 1024);
        let speedStr = ""
        if(number >= 1024) {
          speedStr = (number/1024).toFixed(2) + 'GB'
        } else if(1 <= number && number < 1024) {
          speedStr = number + 'MB'
        } else if(number < 0 || this.speed < 0) {
          speedStr = 0 + 'KB';
        } else {
          speedStr = (this.speed).toFixed(2) + 'KB'
        }
        this.uploadAllData = {
          id: this.currentUploadId,
          isStop: false,
          isSuccess: false,
          msg: '正在下载',
          name: name,
          size: fileSize,
          speed: speedStr,
          percent: this.progress == 100? 99: this.progress,
          type: 1
        };
        this.progressBarWidth = `${this.progress}%`
      }
    })
    .then((res) => {
      // console.log(res);
      if(res && res.data && res.data.code == '10000' && res.data.data) {
        if(this.timer) {
          clearTimeout(this.timer)
        }
        this.uploadedChunks++
        // 上传下一个分片
        this.uploadChunk(chunks, this.uploadedChunks, chunkSize, name, fileSize);
      } else if(res && res.data && res.data.code == '10000' && !res.data.data) { // 重新上传当前分片
        this.uploadChunk(chunks, this.uploadedChunks, chunkSize, name, fileSize);
      } else if(res && res.data && res.data.code == '40004') {
        this.uploadAllData.isStop = !this.uploadAllData.isStop;
        return
      } else if(res && res.data && res.data.code == '50010') {
        this.uploadAllData.isStop = !this.uploadAllData.isStop;
        return
      } else {
        // 一般是网络问题,隔几秒继续上传
        this.timer = setTimeout(() => {
          this.uploadChunk(chunks, this.uploadedChunks, chunkSize, name, fileSize);
        }, 2000);
      }
    })
    .catch((error) => {
      // 处理上传错误
      console.log('上传错误', error)
    })
},

js – 暂停\启动
!说明:因为前端不能做到真正的把请求暂停,只能调用接口,然后后台去实际暂停上传的接口

// 暂停、启动
changeStop() {
  if(this.timer) {
    clearTimeout(this.timer)
  }
  this.uploadAllData.isStop = !this.uploadAllData.isStop;
  if(this.uploadAllData.isStop) { // 点击了启动图标,触发暂停
    httpGet("/api/v1/acctm_file_transmit_task/pauseStart/" + this.currentUploadId)
      .then(res => {
        if(res && res.code == '10000' && !res.data) {
          // console.log('暂停成功!');
        }
      })
  } else { // 点击了暂停图标,触发启动
    httpGet("/api/v1/acctm_file_transmit_task/pauseStart/" + this.currentUploadId)
      .then(res => {
        if(res && res.code == '10000') {
          let num = (this.fileSize/(1024*1024)).toFixed(2); // B => MB
          let fileSizeStr = "";
          if(num >= 1024) {
            fileSizeStr = (num/1024).toFixed(2) + 'GB'
          } else if(1 <= num && num < 1024 ) {
            fileSizeStr = num + 'MB'
          } else {
            fileSizeStr = (num*1024).toFixed(2) + 'KB'
          }
          // 从之前的分片
          this.uploadChunk(this.chunks, this.uploadedChunks+1, this.chunkSize, this.fileData.name, fileSizeStr);
        }
      })
  }
},

js – 删除任务

changeClose() {
  if(this.timer) {
    clearTimeout(this.timer)
  }
  httpDeleter('/api/v1/acctm_file_transmit_task/' + this.currentUploadId)
    .then(res => {
      if(res && res.code == '10000') {
        this.$message({
          type: 'success',
          message: '删除成功!'
        });
        this.initUpload();
      }
    })
},

js – 关闭弹窗
!说明: 由于上传的文件比较大,本地文件存储的位置获取不到,所以如果半路退出,则不能再继续获取到上一次的文件

closeUploadPop() {
  if(this.timer) {
    clearTimeout(this.timer)
  }
  // 判断是否上传完成
  if(this.fileData && !this.uploadAllData.isSuccess) {
    this.$confirm('文件上传未完成,关闭弹窗则上传无效,是否确定关闭?', '温馨提示', {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning'
    })
      .then(async() => {
        let res = await httpDeleter('/api/v1/acctm_file_transmit_task/' + this.currentUploadId)
        if(res && res.code == '10000') {
          this.uploadPop = false;
          this.initUpload();
          if(this.activeName == "1"){
            this.getCloudData(1,10,1,this.algorithmId);
          } else {
            this.getCloudData(1,10,2,this.algorithmId);
          }
        }
      })
      .catch(() => {
        
      })
  } else {
    this.uploadPop = false;
    // 格式化数据
    this.initUpload();
    if(this.activeName == "1"){
      this.getCloudData(1,10,1,this.algorithmId);
    } else {
      this.getCloudData(1,10,2,this.algorithmId);
    }
  }
},

完成、效果展示
正在上传
大文件上传,前端vue 做分片上传_第1张图片
暂停
大文件上传,前端vue 做分片上传_第2张图片
退出
大文件上传,前端vue 做分片上传_第3张图片
完成
大文件上传,前端vue 做分片上传_第4张图片

中秋国庆的八天假 迎来了七天的搬砖!

你可能感兴趣的:(vue.js,upload,分片上传,vue.js,javascript,upload,大文件分片上传,分片上传)