ant-design-vue文件上传中的beforeUpload方法和change方法

ant-design-vue文件上传中的beforeUpload方法和change方法

使用vue+ant-design时,遇到了点小问题,文件上传的过程中,如果文件不立马上传,需要在beforeUpload方法中返回false。我以为只要在beforeUpload方法中返回false后,change方法便不再执行,但事实上并不是这样子的。

一些背景

我的需求是,多个文件上传,然后对单个文件大小作限制,如果大于20M的话就去掉不上传,仅上传小于20M的文件。

先说下beforeUpload和change方法的关系,beforeUpload在返回boolean值的时候Boolean值代表的是文件是否马上上传,即是不需要另外点击提交按钮即上传,

但是注意的一点是无论beforeUpload中返回的是false还是true,change方法都一定会执行。

开始时不了解这两个方法,因为不熟悉前端,以为只要在beforeUpload中这样写就可以实现文件大小的规范

          //对单个文件大于20M进行过滤
          if (!isLt20M) {
            this.$message.error('所选文件单个超过20M,已自动过滤,请检查~');
            return false;
          }

然后在change方法中直接将参数的fileList的属性赋值给自定义的fileList中

	  handleChange(info) {
          this.fileList = info.fileList;
      },

然后出现了问题:当上传大于20M的文件时,确实出现了不能超过20M的提示,但是文件依旧存在于文件列表中,当提交给后端的时候,后端依旧接收到大于20M的文件,也就意味着,文件依旧上传成功。

其实稍微懂点逻辑的都应该知道,必然会导致这种结果,因为在站在对象层面看,beforeUpload方法中,仅仅是打印了提示,并没有对fileList这个对象进行修改,那么自然就不会对结果有所影响。

既然如此,那就应该想怎么在beforeUpload中对文件列表进行修改。前辈告诉我,beforeUpload方法有两个参数,分别是file和fileList,后面通过debugger发现beforeUpload方法中

  • 每个文件上传前都会执行beforeUpload方法,其file参数就是该文件本身。
  • 多文件上传时,fileList就是选择的文件列表,例如一次选择3个文件,那么所选的3个文件就是fileList。
  • 如果在beforeUpload中不对fileList进行修改,那么fileList时不变的,而file是不断在变化的。举个例子,一次选择3个文件上传,那么会执行三次beforeUpload方法,第一次执行方法的file参数就是第一个文件,第二次执行方法的file参数是第二个文件,第三个file就是第三个文件。但是,在三次执行方法的fileList参数中,fileList都是选择的三个文件,他的length都是3。

那么既然知道了beforeUpload的参数了,就很容易对beforeUpload进行操作,按道理只要将不符合的file从fileList中去掉就好了。事实确实如此。

     if (!isLt20M) {
         //将fileList中超过20M的file去掉。
        fileList.splice(fileList.indexOf(file),1);
        this.$message.error('所选文件单个超过20M,已自动过滤,请检查~');
        return false;
      }

这样就从fileList中将file移除了。

了解了beforeUpload后再看看change方法。

之所以将beforeUpload和change方法作比较,是因为这两者确实是有可比之处的。例如,两者都在文件上传之前进行,多文件上传的时候,两者都是每个文件执行一次,两者都可以获取当前文件以及文件列表。但是,beforeUpload和change也有不同之处。例如:

  • change方法和beforeUpload方法一样,每个文件上传都要执行。但是change方法执行在beforeUpload之后。
  • change方法只有一个默认参数,假如用info代替,可以通过info.file获取当前文件、通过info.fileList获取已上传或者需要上传的文件列表。
  • change中的info.fileList和beforeUpload的fileList不一样,info.fileList获取到的是当前文件列表中的内容,即存在于a-upload组件中的所有文件,而beforeUpload中的fileList获取到的是本次选中的文件。(解释起来有点麻烦,但是通过图说明就简单很多,详见下图1)
  • change的info.file.status属性可以判断文件状态,其状态一般有uploading、done、removed、error还有undefined,当beforeUpload返回为false的时候,状态为undefined,当beforeUpload返回为true的时候,每个文件会执行两次change方法,其状态分别为uploading和done状态。

ant-design-vue文件上传中的beforeUpload方法和change方法_第1张图片

由上,还需要注意一个注意点,就是change方法可能会被执行两次,甚至会被执行三次,beforeUpload返回true,但是上传失败的话,还有个error状态,会再次执行。

好了,说完两者的区别,来了解一下两者的执行顺序。

执行顺序会根据beforeUpload返回值的不同而不同(这个真不简单),以下均以多文件上传为例。

  • 如果beforeUpload返回值都为true时,会先执行完所有文件的beforeUpload方法,再依次执行所有的change方法。例如有3个文件,那么会最先执行三次beforeUpload,然后再执行6次change
  • 如果beforeUpload返回值都为false时,会先执行一次beforeUpload方法,然后执行一次change方法,然后再执行一次beforeUpload方法,然后执行一次change方法,然后再执行一次beforeUpload方法,然后执行一次change方法。
  • 如果返回分别是false-true-false的话,那么执行顺序是beforeUpload - change - beforeUpload - beforeUpload - change - change - change - change

是不是很复杂,如果理解不过来的话,有机会建议亲自debugger一下,慢慢来就理解其逻辑了。

就是如果beforeUpload中返回true的话,那么会将该file参数的change方法先搁置,继续执行下一个file的beforeUpload方法,直到有beforeUpload返回false的file,才执行 该file 的change方法(注意是该file的change方法,之前beforeUpload的返回true的change方法依旧没有执行。)直到所有的file的beforeUpload方法全执行完,才开始执行那些beforeUpload返回true的change方法。见流程图(画得太丑见谅)

ant-design-vue文件上传中的beforeUpload方法和change方法_第2张图片

大概就这样,没啥好说了。

实现需求

嗯~还是以需求为主线,回到需求

我的需求是,多个文件上传,然后对单个文件大小作限制,如果大于20M的话就去掉不上传,仅上传小于20M的文件。

还是一样在beforeUpload中作限制,但是要在beforeUpload中控制最终的文件列表,所以需要用到fileList参数

	beforeUpload(file,fileList) {
      //判断是否小于20M
      let isLt20M = file.size < 1024 * 1024 *20;
      if (!isLt20M) {
        //将fileList中本次file文件删掉
        fileList.splice(fileList.indexOf(file),1);
        this.$message.error('所选文件单个超过20M,已自动过滤,请检查~');
      }else {
        //判断选择文件个数是否大于10个
        if(fileList.length>10){
          //将10个之外的文件去除
          fileList.splice(10);
          this.$message.error('所选文件超过10个,超出部分已自动过滤,请检查~');
        }
      }
      return false;
    },

因为在beforeUpload中已经将文件过滤了,再到change方法的时候,info.fileList中已经不存在被过滤掉的文件,所以不需要在change中再执行过滤操作,甚至乎直接将info.fileList赋值给自定义的参数fileList就好(自定义的fileList是用于记录用户要上传的file列表,用于在表单提交时一并提交给后端。)change方法如下:

	handleChange(info) {
      //将最终的文件列表赋值给自定义的fileList容器。
      this.fileList = info.fileList;
      //如果beforeUpload中返回的是false的话,那么
      //info.file.status === "undefined"
      if (info.file.status === "uploading") {
        this.$message.success(`${info.file.name} 正在上传`);
      } else if (info.file.status === "done") {
        this.$message.success(`${info.file.name} 上传成功`);
      } else if (info.file.status === "error") {
        this.$message.error(`${info.file.name} 文件上传失败`);
      }
    },

一些题外的话

其实在以上两个方法中,代码并不算完整,主要是在对文件个数的控制上,由于beforeUpload的fileList是当前选择的文件列表,那么,第一次选了5个文件,第二次还能选10个文件,它只是对单次选择的文件作限制,而没有对所有文件。

这个问题的解决办法并不难,只要在change中将文件个数限制一下就行。因为change方法中的info.fileList就是所有文件列表。

难点在于编辑上,上头让我实现编辑页面的文件修改,例如删除不想要的文件,添加新的文件,最后再一并通过表单提交到后端。由于数据库设计和后端代码接口设计的问题,需要我将要删除的文件uid作数组传给后端,添加文件的实体object列表传给后端,而不是将最后真是生效的文件列表传给后端。

问题的难点在于对文件个数的控制上,我需要记录数据库传过来的文件个数,要删除的文件个数,要添加的文件个数,然后最终的文件个数不能超过20个,还要控制用户可能选择添加之后再进行删除的文件,要对这些数据进行协调,emmm,说实话,听起来简单,做起来头大。最后一顿胡乱摸索好像解决了,但是实现过程中是根据调试结果进行的代码修改,例如添加了一个

		  let cutIndex = fileList.indexOf(file);
          if (cutIndex!==-1) {
            fileList.splice(cutIndex);
          }

但是实话,我不知道为什么需要添加这个判断,代码有点乱,解决方法很极端,在此就不贴了,后期如果有时间再整理一下,想想更好的解决方案。

你可能感兴趣的:(前端学习,ant-design,vue.js)