使用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的参数了,就很容易对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也有不同之处。例如:
info.file
获取当前文件、通过info.fileList
获取已上传或者需要上传的文件列表。a-upload
组件中的所有文件,而beforeUpload中的fileList获取到的是本次选中的文件。(解释起来有点麻烦,但是通过图说明就简单很多,详见下图1)info.file.status
属性可以判断文件状态,其状态一般有uploading、done、removed、error还有undefined,当beforeUpload返回为false的时候,状态为undefined,当beforeUpload返回为true的时候,每个文件会执行两次change方法,其状态分别为uploading和done状态。由上,还需要注意一个注意点,就是change方法可能会被执行两次,甚至会被执行三次,beforeUpload返回true,但是上传失败的话,还有个error状态,会再次执行。
好了,说完两者的区别,来了解一下两者的执行顺序。
执行顺序会根据beforeUpload返回值的不同而不同(这个真不简单),以下均以多文件上传为例。
是不是很复杂,如果理解不过来的话,有机会建议亲自debugger一下,慢慢来就理解其逻辑了。
就是如果beforeUpload中返回true的话,那么会将该file参数的change方法先搁置,继续执行下一个file的beforeUpload方法,直到有beforeUpload返回false的file,才执行 该file 的change方法(注意是该file的change方法,之前beforeUpload的返回true的change方法依旧没有执行。)直到所有的file的beforeUpload方法全执行完,才开始执行那些beforeUpload返回true的change方法。见流程图(画得太丑见谅)
大概就这样,没啥好说了。
嗯~还是以需求为主线,回到需求
我的需求是,多个文件上传,然后对单个文件大小作限制,如果大于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);
}
但是实话,我不知道为什么需要添加这个判断,代码有点乱,解决方法很极端,在此就不贴了,后期如果有时间再整理一下,想想更好的解决方案。