vue实现简单的分片上传

简单分片上传的实现

一、实现逻辑

①选择文件,将文件后缀传给后端,获取该文件的ID(在后面向后端传每个分片时,都需要将这个ID带上);②根据文件大小判断是否需要上传(本文以5M为例),③如需,将文件进行切片④将文件转换成formData格式⑤分组上传切片(如果一个切片上传3次,就放弃上传)⑥所有切片上传完成,合并切片。

二、实现样式

在这里插入图片描述

三、实现代码

1.样式(iviewui组件)

       <div v-if="!formItem.url">
            <Upload :before-upload="handleBeforeUpload" action="">
              <Button icon="ios-cloud-upload-outline">
                选择文件上传
              </Button>
            </Upload>
            <div v-if="file !== null">
              <div>
                <span>{{ file.name }}</span>
                <Icon type="ios-trash-outline" @click="removeHandle()"/>
              </div>
              <Button @click="upload(file)" :loading="loadingStatus" >
                {{ loadingStatus ? "上传中..." : "点击确认上传" }}
              </Button>
            </div>
          </div>
          <div v-else>{{ formItem.url }}
            <Icon v-if="title != 'detail'" type="ios-trash-outline" @click="removeHandle()"/>
          </div>

2.获取文件后缀

    handleBeforeUpload(file) {
      this.file = file;
      let str = "";
      str = this.file.name.substr(this.file.name.lastIndexOf('.') + 1);
      if (str) {
        createUploadId({
          fileSuffix: str,
        }).then((res) => {
          if (res.data.code == 200) {
            //获取分片上传文件的id
            this.uploadId = res.data.data;
          } else {
            this.uploadId = ''
          }
        });
      }
    },

3.上传分片

   async upload(file) {
      this.loadingStatus = true;
      //1.先判断拥有分片上传的id
      if (this.uploadId) {
        //2.分片
	let ChunkSize= 5 * 1024 * 1024;
        const chunkList = this.createFileChunk(file,ChunkSize)  //切割分片函数
	3.处理切片
        this.requestList = chunkList.map(({ file }, index) => ({
          file,
          index,
          partSize: file.size,//当前片大小
          partNumber: index + 1,//当前片数
          totalPart: chunkList.length,//总片数
          uploadId: this.uploadId,//分片上传id
        }));
        await this.uploadChunks(); // 执行上传切片的操作
      } else {
        this.$Message.info("分片上传Id不能为空,请删除文件重试!");
        this.loadingStatus = false;
      }

    },
3.1 切割分片
    createFileChunk(file, size) {
      //切割文件,size是每片的大小
      const fileChunkList = []
      let cur = 0
      while (cur < file.size) {
        // 使用slice方法切片
        fileChunkList.push({ file: file.slice(cur, cur + size) })
        cur += size
      }
      return fileChunkList
    },
3.2 上传每个分片
 async uploadChunks(uploadedList = []) {
      //转成formData格式 
      const requestList = this.requestList
        .filter((chunk) => !uploadedList.includes(chunk.file))
        .map(({ file, partSize, partNumber, uploadId, totalPart, index }) => {
          console.log('item==', file, partSize, partNumber, uploadId, totalPart, index);
          const formData = new FormData();
          formData.append("file", file);
          formData.append("partSize", partSize);
          formData.append("partNumber", partNumber);
          formData.append("totalPart", totalPart);
          formData.append("uploadId", uploadId);
          return { formData, index };
        });

      await this.sendRequest(requestList, 4);

      // chunk 全部发送完成了需要通知后台去合并切片
      if (uploadedList.length + requestList.length === this.requestList.length) {
        await this.mergeChunks();
      }


    },
3.3 控制并发数量
    async sendRequest(forms, max) {
        //forms,切片列表
        //max最大并发数
      //Status,每个切片的状态
      const Status = { wait: 1, error: 2, done: 3, fail: 4 };
      return new Promise((resolve, reject) => {
        const len = forms.length; //长度
        let counter = 0; // 已经发送成功的请求
        const retryArr = []; // 记录错误的次数
        // 一开始将所有的表单状态置为等待
        forms.forEach((item) => (item.status = Status.wait));
        const start = async () => {
          // 有请求,有通道
          while (counter < len && max > 0) {
            max--; // 占用通道
            // 只要是没有完成的我们就重发
            let idx = forms.findIndex((v) => v.status == Status.wait || v.status == Status.error);
            if (idx == -1) {
              // 找不到失败状态和等待状态
              return reject();
            }
            let { formData, index } = forms[idx];
          
            await UZadeefillnoop(

              formData,
            ).then((res) => {
              console.log('res.data.data', res.data.code);
              if (res.data.code == 200) {
                forms[idx].status = Status.done;
                max++; // 释放通道
                counter++;
                if (counter === len) {
                  resolve();
                }
              } else {
                forms[idx].status = Status.error;
                if (typeof retryArr[index] !== 'number') {
                  this.$message.error(`第 ${index} 个块上传失败,系统准备重试`);
                  retryArr[index] = 0;
                }
                // 次数累加
                retryArr[index]++;
                // 一个请求报错3次的
                if (retryArr[index] > 3) {
                  this.$message.error(`第 ${index} 个块重试多次无效,放弃上传`);
                  forms[idx].status = Status.fail;
                }
                max++; // 释放通道
              }


            }).catch(() => {
              forms[idx].status = Status.error;
              if (typeof retryArr[index] !== 'number') {
                this.$message.error(`第 ${index} 个块上传失败,系统准备重试`);
                retryArr[index] = 0;
              }
              // 次数累加
              retryArr[index]++;
              // 一个请求报错3次的
              if (retryArr[index] > 3) {
                this.$message.error(`第 ${index} 个块重试多次无效,放弃上传`);
                forms[idx].status = Status.fail;
              }
              max++; // 释放通道

            });

          }
        };
        start();
      });
    },
3.4 合并切片
    mergeChunks() {
      let obj = {}
      obj.uploadId = this.uploadId
      completeUpload(obj).then((res) => {
   
        if (res.data.code == 200) {
          this.$Message.success("上传成功");
          for (let item of this.$refs.formItem.fields) {
            if (item.prop === "url") {
              item.resetField();
              break;
            }
          }
          this.formItem.url = res.data.data;
          this.loadingStatus = false;
        }
      }).catch(() => {

      })
  
    },
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值