背景
相关版本信息:
React:16.12 ( 使用class组件 )
Antd:2.x
项目中有附件上传的功能,现在要增加一个重复上传文件的校验需求。由于上传时后端没有做任何校验,于是需要前端对上传的文件和历史上传文件列表进行比对,若用户上传同名文件,则默认为重复上传,需要前端进行拦截和提示。
做法思路:使用Antd上传组件中的beforeUpload钩子对用户上传的文件进行校验,在这之前需要请求一次后台接口,获取历史附件信息的对象数组,通过对比文件名,进行校验。
踩坑:笔者以为beforeUpload在接收Promise对象时,会根据resolve中的布尔值进行判断,是否继续上传。但事实上,beforeUpload如果返回的是一个Promise对象时,是根据resolve还是reject进行判断的,并不关心返回的值是什么。
Upload 组件
本文主要聚焦于beforeUpload钩子,现简要贴出官网API
| 参数 | 说明 | 类型 | 默认值 | |
|---|---|---|---|---|
| beforeUpload | 上传文件之前的钩子,参数为上传的文件。若返回false,则停止上传。支持返回一个Promise对象,Promise对象reject时则停止上传,resolve时开始上传。resolve传入File或Blob对象则上传resolve传入对象。 | (file,fileLise) => `boolean | Promise` | 无 |
重复文件校验逻辑
上传前请求一次后台接口,得到历史附件列表的对象数组,将准备上传的附件与历史附件列表取交集,如果交集存在,则判断为重复。
/*
* @description 重复文件校验
* @param file {Object} beforeUpload钩子函数的第一个入参
* @returns Promise
*/
validataAttachmentInfo = async (file) => {
const { params: {orgCode = "", ref1 = ""}} = this.props; // 查询历史附件所需参数
let fileInfoList = await this.getUploadAttachmentsHistory(orgCode, ref1);
let fileName = `${file.name.replace(/\s+/g, "")}`;
if(checkDuplicateAttachment(fileInfoList, fileName)){
Moal.info({title:"提示", okText:"确定", content: `已存在文件${fileName},请勿重复上传`});
return Promise.reject(false);
}
return Promise.resolve(true);
}
checkDuplicateAttachment = (fileList = [], uploadFileName = "") => fileList.some(file => file.fileName === uploadFileName);
Upload组件的使用和prop的配置
组件的使用
<Upload {...this.prop} name={this.state.fileName} data={this.state.reqData}>
<Button
type="primary"
>
附件上传
</Button>
</Upload>
prop中beforeUpload的配置
错误配置
async 函数返回一个Promise,这样做,无论如何返回的都是resolve状态,只是resolve里面的值是false或者true。
导致校验成功,但无法终止上传
prop = {
beforeUpload: async (file) => {
try {
let fileName = `${file.name.replace(/\s+/g, "")}`;
let allowUpload = await this.validataAttachmentInfo(file);
if(allowUpload){
// 上传按钮loading
this.setState({loading: true, fileName});
// 封装data,更新this.state.reqData
this.getreqData(fileName);
}
return allowUpload
} catch (error){
return false
}
}
}
解决方法:
如果需要返回一个Promise对象,需要通过返回值是resolve还是reject来控制是否继续上传。
1. 不使用async/await,使用promise
prop = {
beforeUpload: (file) => {
return new Promise((resolve,reject) => {
let fileName = `${file.name.replace(/\s+/g, "")}`;
this.validataAttachmentInfo(file).then(allowUpload => {
if(allowUpload){
this.setState({loading: true, fileName});
this.getreqData(fileName);
allowUpload ? resolve(); reject(); // 抛出promise
}
}).catch(() => {
reject(); // 请求失败
});
return allowUpload
})
}
}
- 缺点:破坏了函数结构
2. 使用async/await
prop = {
beforeUpload: async (file) => {
let fileName = `${file.name.replace(/\s+/g, "")}`;
let allowUpload = await this.validataAttachmentInfo(file);
if(allowUpload){
// 上传按钮loading
this.setState({loading: true, fileName});
// 封装data,更新this.state.reqData
this.getreqData(fileName);
}
return await (allowUpload ? Promise.resolve() : Promise.reject()); // 抛出Promise
}
}
最后
会踩这个坑很大的原因在于我没有仔细看文档,看文档真的很重要。
谢谢大佬帮我分析出问题所在。