el-upload 多文件依次上传(防抖 + 递归)

需求描述

  • 多图上传组件,1-9 张图
  • 选择完文件后自动上传,不需要上传按钮来进行手动上传

难点

  • 接口有两种,多图集合上传接口 uploadImgs、单图上传接口 uploadImg
    • 使用 uploadImgs 接口,参数为图片集合 fileList,但是缺少上传按钮,无法监听用户选择完图片的时间点
    • 使用 uploadImg 接口,参数为图片 file,接口被多次同时调用造成服务器压力

解决思路

  • 关闭 auto-upload 属性,手动控制上传进程,监听组件的 change 事件
  • 使用防抖函数,监听 change 事件
  • 选择多张图片后,组件每读取到一张图片就会调用一次 change 事件
  • change 事件被触发后的某时间段内如果没有再次被触发,视为用户此次选择图片结束
  • 获取待上传的文件列表,将图片集合传递给后台
  • 如果调用单图上传,需要加入递归,重复多次调用上传接口,将待上传文件列表中的内容逐一上传
<el-upload
  action=""
  list-type="picture-card"
  multiple
  show-file-list
  :on-change="changeFile"
  :on-exceed="exceedFile"
  :auto-upload="false"
  :file-list="fileList"
  :limit="9"
  :disabled="disabled"
  ref="upload"
>
  <i class="el-icon-plus"></i>
</el-upload>
data() {
  return {
    timmer: null, // 用于控制防抖的定时器
    fileList: [], // 文件列表--上传结束后回显用
    disabled: false, // 控制组件是否禁用
    uploadFileList: [], // 上传文件列表--待传递
    successFileUrls: [], // 记录上传成功的文件列表--用不到,这里为了方便演示
  };
},
// 防抖函数
debounce(func, delay) {
  return (() => {
    if (this.timmer) {
      // 如果已经存在,说明之前执行过了,这里要清除,重新开始
      console.log("%c清理定时器的操作~", "color: blue;");
      clearTimeout(this.timmer);
    }
    this.timmer = setTimeout(func, delay);
  })();
},
changeFile(file, fileList) {
  console.log("触发组件change事件", file, fileList);
  // 开启禁用开关
  this.disabled = true;
  this.debounce(() => {
    this.uploadFileList = JSON.parse(JSON.stringify(fileList)); // 文件列表赋值
    // this.uploadImg(); // 执行上传事件--单图上传
    this.uploadImgs(); // 执行上传事件--多图上传
    this.timmer = null;
  }, 1000);
},
exceedFile() {
  this.$message.error("上传文件最多9个");
},
// 上传事件--多图上传
uploadImgs() {
  console.log("%c模拟接口执行上传事件----开始上传", "color: red;");
  console.log(this.uploadFileList);
  if (this.uploadFileList.length > 0) {
    this.mockFunc(2, this.uploadFileList)
      .then((res) => {
        console.log("%c模拟接口执行上传事件----成功", "color: green;");
        this.successFileUrls.push(res);
        console.log("当前已上传成功的文件", this.successFileUrls);
        // 实际应用接口,返回文件 url 集合,用 url 集合结果覆盖掉 fileList
        // fileList 被返回结果覆盖掉之后,也会同时清理掉组件中上传失败的文件
        // this.fileList = this.successFileUrls;
      })
      .catch(() => {
        console.log(
          "%c模拟接口执行上传事件----失败",
          "color: yellowgreen;"
        );
      });
  }
},
// 上传事件--单图上传
uploadImg() {
  console.log("%c模拟接口执行上传事件----开始上传", "color: red;");
  console.log(this.uploadFileList);
  if (this.uploadFileList.length > 0) {
    this.mockFunc(1, this.uploadFileList[0])
      .then((res) => {
        console.log("%c模拟接口执行上传事件----成功", "color: green;");
        this.successFileUrls.push(res);
        console.log("当前已上传成功的文件", this.successFileUrls);
        // 实际应用接口,返回文件 url 集合,用 url 集合结果覆盖掉 fileList
        // this.fileList = this.successFileUrls;
        this.uploadFileList.shift(); // 上传成功后,删除待传递列表的第一个
        this.uploadImg(); // 递归,执行下一个操作
      })
      .catch(() => {
        console.log(
          "%c模拟接口执行上传事件----失败",
          "color: yellowgreen;"
        );
      });
  } else {
    console.log("待上传文件列表全部传递完毕");
    // 关闭禁用开关
    this.disabled = false;
  }
},
 // 模拟接口--手动延时、返回正确或错误结果
mockFunc(type, file) {
  if (type == 1) {
    // 模拟单图上传
    return new Promise((resolve, reject) => {
      console.log("上传的文件为", file.name);
      let params = new FormData();
      params.append("file", file);
      console.log("file", file);
      setTimeout(() => {
        let radio = file.size / 1024 / 1024;
        if (radio <= 2) {
          // 接口得到图片云存储的路径 url,这里用 name 代替
          resolve(file.name);
        } else {
          reject();
        }
      }, 200);
    });
  } else {
    // 模拟多图上传
    return new Promise((resolve, reject) => {
      let params = new FormData();
      let fileName = []; // 模拟接口返回值,实际用不到
      file.forEach((item) => {
        let radio = item.size / 1024 / 1024;
        if (radio <= 2) {
          params.append("file", item);
          // 接口得到图片云存储的路径 url,这里用 name 代替
          fileName.push(item.name);
        }
      });
      console.log("file", file);
      setTimeout(() => {
        resolve(fileName);
      }, 200);
    });
  }
},

结果演示

  • 未调用实际接口,在 mockFunc 中记录文件名称,来模拟后台接口响应中返回的文件云存储路径。
  • 调用接口结束后,使用接口响应中返回的云存储路径覆盖掉 fileList,使上传组件回显的图片集合,与上传成功后的图片集合保持一致

调用 uploadImgs 多图上传

  • 选择三张小于 2M 的图片上传
  • change 事件被触发 3 次,在第 3 次结束后,1000ms 内,未被再次触发,开始调用上传接口
  • 上传接口调用成功,返回结果

在这里插入图片描述

  • 选择两张小于 2M 的图片,一张大于 2M 的图片上传
  • change 事件被触发 3 次,每触发一次,向待上传数组 uploadFileList 中塞一个 file 对象,在第 3 次结束后,1000ms 内,未被再次触发,开始调用上传接口,将 uploadFileList 传递给后台
  • 上传接口调用成功,返回结果,结果集合的长度为 2,表示上传图片列表中有一张上传失败了,在实际接口应用中,使用接口响应结果覆盖掉 el-upload 绑定的 fileList 即可

在这里插入图片描述

调用 uploadImg 单图上传

  • 选择三张小于 2M 的图片上传
  • change 事件被触发 3 次,每触发一次,向待上传数组 uploadFileList 中塞一个 file 对象,在第 3 次结束后,1000ms 内,未被再次触发,开始递归调用上传接口
  • 假设 uploadFileList = [img1,img2,img3] ,此时 uploadFileList 长度为 3,表示当前有 3 张图片待上传,调用接口,传递 uploadFileList[0] 对象,即 img1,调用结束后,将 uploadFileList 第一项删除,此时 uploadFileList = [img2,img3]
  • 继续调用上传接口,此时 uploadFileList 长度为 2,表示当前有两张图片待上传,调用接口,传递 uploadFileList[0] 对象,即 img2,调用结束后,将 uploadFileList 第一项删除,此时 uploadFileList = [img3]
  • 继续调用上传接口,此时 uploadFileList 长度为 1,表示当前有 1 张图片待上传,调用接口,传递 uploadFileList[0] 对象,即 img3,调用结束后,将 uploadFileList 第一项删除,此时 uploadFileList = []
  • 继续嗲用上传接口,此时 uploadFileList 长度为 0,表示待上传图片已经全部上传完毕,结束上传进程

在这里插入图片描述

  • 选择两张小于 2M 的图片,一张大于 2M 的图片上传
  • 递归流程同上
  • 上传接口调用成功,返回结果,结果集合的长度为 2,表示上传图片列表中有一张上传失败了,在实际接口应用中,使用接口响应结果覆盖掉 el-upload 绑定的 fileList 即可

在这里插入图片描述

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
el-upload组件可以用于上传文件流。根据引用\[1\]中的描述,你可以使用递归的方式一次上传一个文件,等待前一个文件上传成功或失败后再上传下一个文件。这样可以减小服务器的压力,但上传时间会比较长。在el-upload组件中,你可以使用before-upload钩子来进行文件上传前的参数校验。根据引用\[3\]中的描述,你可以在beforeUpload方法中判断相关参数是否符合校验,如果不符合则返回false停止上传,如果符合则返回true继续上传。在handleAvatarSuccess方法中,你可以处理上传成功后的逻辑,比如将文件转为URL地址。这样就可以实现el-upload上传文件流的功能。 #### 引用[.reference_title] - *1* [el-upload文件上传el-upload采用递归依次上传文件el-upload采用递归在上一个文件上传成功后再传下一...](https://blog.csdn.net/i_am_a_div/article/details/127431603)[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^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [el-upload 上传文件的使用方式(总结)](https://blog.csdn.net/qq_33404590/article/details/130510848)[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^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值