# el-upload 的 before-upload 绑定函数 内部使用异步语句的问题

业务文件位置: 信息流广告平台项目\src\components\SingleFileUpload\index.vue

  • 问题描述: before-upload 绑定函数使用---在重复文件检测条件满足时,pass_vaild 也变为 false 的情况下,还是调用了下一步的 http-request 函数

  • 出问题的业务代码

async onBeforeUpload(file) {
  let pass_valid = true;

  const { size } = file;
  if (size > this.fileSizeLimit) {
    this.$message.error(
      `上传${TYPE_MAPPER[this.uploadType]}大小不能超过 ${
        this.uploadFileSizeLimit
      }MB!`
    );
    pass_valid = false;
  }

  if (!pass_valid) return pass_valid; // 如果已经确定不通过,则直接返回

  // 重复文件检测
  if (this.uploadFileAmountLimit > 1 && this.list.length) {
    let exist_file_md5s = this.list.map((file) => file.md5);
    const cur_file_md5 = await promisefyMd5(file);

    if (exist_file_md5s.includes(cur_file_md5)) {
      this.$message.warning("请勿重复上传相同文件!");
      pass_valid = false;
    }
  }

  console.log(`[Dev_Log][${"pass_vaild"}_]_>>>`, pass_valid);
  return pass_valid;
},
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# el-upload 源码位置 解读

element-ui\packages\upload\src\upload.vue 或者编译文件 element-ui\lib\element-ui.common.js // 29523 行

// 84 行
upload(rawFile) {
  this.$refs.input.value = null;

  if (!this.beforeUpload) {
    return this.post(rawFile);
  }

  // 执行上传前处理函数得到返回结果
  const before = this.beforeUpload(rawFile);

  // 上传前处理函数为异步函数,返回promise
  if (before && before.then) {
    // promise((rs,rj)=>{
        //...
    // }).then(onFulfilled, onRejected) // then 接送两个回调函数,已完成回调,已拒绝回调
    before.then(function (processedFile) { // 已完成回调
      var fileType = Object.prototype.toString.call(processedFile);
      
      if (fileType === '[object File]' || fileType === '[object Blob]') {
        // ... 其他文件处理
        _this2.post(processedFile);
      } else {
        _this2.post(rawFile);
      }
    }, function () { // 已拒绝回调
      _this2.onRemove(null, rawFile);
    });
  } 
  // 上传前处理函数为同步函数,返回Booolean值
  else if (before !== false) {
      this.post(rawFile);
    } else {
      this.onRemove(null, rawFile);
    }
  },
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

# 原因分析

  • 使用 aynsc 绑定的 onBeforeUpload 函数,会走if (before && before.then)

  • 再看 before.then(onFulfilled, onRejected), 因为 async onBeforeUpload 函数中没有抛错逻辑,固定就会走 onFulfilled 默认就会上传了,而不是走onRejected 取消上传

# 案列测试

  • 不做抛错处理
let aaasF = async function handleFun(){
  await Promise.resolve()
}

// // dev-log >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
console.log(`[Dev_Log][${'aaasF'}_]_>>>`, aaasF())

// 输出如下
// [Dev_Log][aaasF_]_>>>  Promise {<pending>}
//                         [[Prototype]]: Promise
//                         [[PromiseState]]: "fulfilled"
//                         [[PromiseResult]]: null
1
2
3
4
5
6
7
8
9
10
11
12
  • 使用throw抛错处理
let aaasF = async function handleFun(){
  await Promise.resolve()
  throw null;
}

// // dev-log >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
console.log(`[Dev_Log][${'aaasF'}_]_>>>`, aaasF())

// 输出如下
// [Dev_Log][aaasF_]_>>>  Promise {<pending>}
//                         [[Prototype]]: Promise
//                         [[PromiseState]]: "rejected"
//                         [[PromiseResult]]: null
1
2
3
4
5
6
7
8
9
10
11
12
13
  • 结合 源码来看 before-upload 可接受异步函数的返回, 若为上诉 async/await 却没有throw报错 直接就满足了before-upload的then(resolve,reject) resolve执行

# 解决方案

# 一:保持 async/await, 但是不满足就直接throw 抛错

  • 加 throw
async onBeforeUpload(file) {
  let pass_valid = true;

  const { size } = file;
  if (size > this.fileSizeLimit) {
    this.$message.error(
      `上传${TYPE_MAPPER[this.uploadType]}大小不能超过 ${
        this.uploadFileSizeLimit
      }MB!`
    );
    pass_valid = false;
  }

  if (!pass_valid) return pass_valid; // 如果已经确定不通过,则直接返回

  // 重复文件检测
  if (this.uploadFileAmountLimit > 1 && this.list.length) {
    let exist_file_md5s = this.list.map((file) => file.md5);
    const cur_file_md5 = await promisefyMd5(file);

    if (exist_file_md5s.includes(cur_file_md5)) {
      this.$message.warning("请勿重复上传相同文件!");
      pass_valid = false;
      throw null; // before-upload 可接受异步函数的返回,中断上传必须 throw
    }
  }

  return pass_valid;
},
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 二:使用异步的链式调用

/** 
 * new_version
 * el-upload  before-upload 可接受异步函数的返回  用 resolve && reject
 */
onBeforeUpload(file) {
  return new Promise(async (resolve, reject) => {
    try {
      let { size } = file;
      if (size > this.fileSizeLimit) {
        // prettier-ignore
        this.$message.error(`上传${TYPE_MAPPER[this.uploadType]}大小不能超过 ${this.uploadFileSizeLimit}MB!`);
        reject(false);
      }

      /** 重复文件检测 */
      // 若上传数量大于 1  && // 若已有上传文件(成功上传的文件都有 MD5 值)
      if (this.uploadFileAmountLimit > 1 && this.list.length) {
        let exist_file_md5s = this.list.map((file) => file.md5);

        // 当前文件MD5获取
        let cur_file_md5 = await promisefyMd5(file);

        if (exist_file_md5s.includes(cur_file_md5)) {
          this.$message.warning("请勿重复上传相同文件!");
          reject(false);
        }
      }

      resolve(true);
    } catch (error) {
      reject(false);
      throw error;
    }
  });
},
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

或者