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

1 篇文章 0 订阅
1 篇文章 0 订阅

html – 以弹窗的形式

<!-- 上传算法文件 -->
<el-dialog title="上传算法文件" :visible.sync="uploadPop" width="60%" :close-on-click-modal="false" :before-close="closeUploadPop" append-to-body custom-class="upload_box">
  <div class="upload_btn_box">
    <el-upload
      class="upload-demo"
      ref="upload"
      action=""
      :show-file-list="false"
      :http-request="requestFile"
      :before-upload = "checkFileType"
      v-if="isShowUploadBtn"
      >
      <el-button>上传压缩包</el-button>
    </el-upload>
    <el-button v-else :loading="true">上传压缩包</el-button>
  </div>
  <div class="tip_box">
    <span class="tips_text">
      1、请上传文件类型为zip的算法文件;
      2、上传可能需要一些时间,上传过程中建议不要关闭弹窗。
    </span>
  </div>
  <div class="file_box">
    <div class="upload_list" v-if="isShowUpload">
      <div id="progress-bar-container" class="list_box" :class="{'isSuccess': uploadAllData.isSuccess}">
        <span style="width: 14px"><i class="el-icon-folder"></i></span>
        <span style="width: 30%">{{ uploadAllData.name }}</span>
        <span style="width: 12%">{{ uploadAllData.type == 1? '算法文件': '测试集文件' }}</span>
        <span style="width: 10%">{{ uploadAllData.size }}</span>
        <span style="width: 16%">{{ uploadAllData.isSuccess?uploadAllData.msg: uploadAllData.isStop? '暂停中': uploadAllData.percent+'%' + ' ' + uploadAllData.speed + '/s' }}</span>
        <span class="action_btn" v-if="!uploadAllData.isSuccess" @click="changeStop">{{ uploadAllData.isStop?'▶': '‖' }}</span>
        <span class="action_btn" v-if="!uploadAllData.isSuccess" @click="changeClose">✖</span>
        <div id="progress-bar" class="progress-bar" :style="{ width: progressBarWidth }"></div>
      </div>
    </div>
  </div>
</el-dialog>

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);
    }
  }
},

完成、效果展示
正在上传
在这里插入图片描述
暂停
在这里插入图片描述
退出
在这里插入图片描述
完成
在这里插入图片描述

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
前端vue文件分片上传是一种将大文件切分成多个块进行上传的技术,通过将文件分成若干小块,逐个上传,可以减少重新上传的开销,并提高上传的效率。这种技术可以应对网络不稳定、传输中断等问题,当网络传输中断时,只需要重新传输剩余的分片,而不需要重新上传整个文件,从而节省了传输时间和成本。为了实现断点续传功能,需要与后台接口配合,后台接口负责接收并保存已上传文件块,以便在后续上传时跳过已上传的部分。对于前端vue文件分片上传,需要后台接口的支持,后台接口需要接收分片并正确组合,这样在上传中断时只需要找到中断的分片,而不用重新上传全部内容,进一步节约成本。具体的实现方案可以根据具体需求和后台接口的支持来进行选择和开发。123 #### 引用[.reference_title] - *1* *2* [Vue实现大文件分片上传,包括断点续传以及上传进度条](https://blog.csdn.net/yjxkq99/article/details/128942133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}} ] [.reference_item] - *3* [前端vue实现大文件分片上传](https://blog.csdn.net/tt18473481961/article/details/128819751)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值