背景

■Ant Design 官方文档中对于限制数量、上传接口请求与响应、下载等功能的自定义实现方法描述得不详细,在此做下记录。
■ 实现功能如下:
(1)上传接口报错时弹出错误信息(由于后端接口统一将错误信息与错误码封装在 http 码为 200 的 body 中,所以需要上传接口请求与响应的自定义);
(2)限制上传文件数量 10 个;
(3)选择文件对话框中可选多个文件;
(4)限制每个文件大小不超过 20M;
(5)上传成功后展示的文件列表项可点击下载;
(6)onChange 传出值格式:[{fileName: string, fileUrl: string}]

代码实例

□ 代码实例如下,经调试发现无论 onSuccess 传入怎样的参数格式,下载功能都需要自己实现。

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { Upload, message, Button } from 'antd';
import axios from 'axios';

const FileUpload = ({
maxFileSize = 20,
maxFileCount = 10,
onChange,
}) => {

// 文件大小限制
const maxFileSizeRef = useRef();
useEffect(() => {
// M转byte
maxFileSizeRef.current = maxFileSize * 1024 * 1024;
}, [maxFileSize]);

// 文件数量记录
const [fileListCount, setFileListCount] = useState(0);
const [isDisabled, setIsDisabled] = useState(false);
useEffect(() => {
if (fileListCount >= maxFileCount) {
setIsDisabled(true);
} else {
setIsDisabled(false);
}
}, [fileListCount, maxFileCount]);

// "文件uid-下载url"映射记录
const [downloadUrls, setDownloadUrls] = useState({});

// onChange格式转换
const customOnChange = useCallback(
({ fileList }) => {
const outputValue = [];
fileList.forEach((one) => {
if (one.status === 'done') {
outputValue.push({
fileName: one.name,
fileUrl: downloadUrls[one.uid],
});
}
});
// console.log('onChange: ', outputValue);
onChange && onChange(outputValue);
},
[onChange, downloadUrls]
);

return (
<Upload
onChange={customOnChange}
showUploadList={{
showDownloadIcon: true,
}}
beforeUpload={(file, fileList) => {
const newCount = fileListCount + fileList.length;
if (file.size > maxFileSizeRef.current) {
message.warn(`文件'${file.name}'超过文件大小限制: ${maxFileSize}M`);
return Upload.LIST_IGNORE;
} else if (newCount > maxFileCount) {
message.warn(`超过最大上传数量: ${maxFileCount}`);
return Upload.LIST_IGNORE;
} else if (newCount === maxFileCount) {
setIsDisabled(true);
}
}}
customRequest={(allData) => {
setFileListCount(fileListCount + 1);
const { onProgress, onSuccess, onError } = allData;
const reqFormData = new FormData();
// 通过组件的data属性传入的额外请求参数,根据接口要求定制
Object.keys(allData.data).forEach((oneKey) => {
reqFormData.append(oneKey, allData.data[oneKey]);
});
reqFormData.append('file', allData.file);
axios({
method: 'post',
url: '/common/file/upload',
data: reqFormData,
onUploadProgress: (progress) => {
// 上传进度条
const progNum = progress.loaded / progress.total;
onProgress({ percent: progNum });
},
})
.then((resData) => {
setDownloadUrls((prevState) => {
return {
...prevState,
[allData.file.uid]: resData.url,
};
});
onSuccess({ url: resData.url });
})
.catch((error) => {
onError(error);
});
}}
multiple={true}
data={(file) => {
return {
typeId: 2,
fileName: file.name,
};
}}
onRemove={() => {
setFileListCount(fileListCount - 1);
}}
openFileDialogOnClick={!isDisabled}
onDownload={(file) => {
window.open(downloadUrls[file.uid]);
}}
>
{isDisabled ? (
<span style={{ color: '#ff7700' }}>
已达到最大上传数量: {maxFileCount}个
</span>
) : (
<Button>上传</Button>;
)}
</Upload>
);
};

... ...

参考文献

rc-upload文档
前端通过 axios 和 FormData 实现文件上传功能遇到的坑