React中Antd Upload组件部分设备无法获取文件类型求助
问题描述
在React项目中使用Antd Upload组件实现文件上传功能,通过beforeUpload属性在上传前校验文件类型,仅允许特定格式文件上传。但部分设备上file.type为空字符串,导致合法文件被误判为不支持类型:
- 最初推测是操作系统问题(MacBook出现异常,Windows正常),但部分Mac设备又能正常工作;
- 同一文件在相同浏览器类型和版本的不同设备上表现不一致,无法定位根因。
相关代码如下:
export const ATTACHMENT_ALLOWED_FILE_TYPES = [ "application/x-zip-compressed", "application/x-rar-compressed", "application/x-compressed", "application/zip", // mac format for zip "application/x-rar", // mac format for rar "application/vnd.rar", // MIME type for .rar files "application/x-compressed", "image/jpeg", "image/png", "image/jpg", "application/pdf", "application/vnd.ms-excel", // Excel 97-2003 "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", // Excel 2007+ "application/vnd.ms-powerpoint", // PowerPoint 97-2003 "application/vnd.openxmlformats-officedocument.presentationml.presentation", // PowerPoint 2007+ "application/msword", // Word 97-2003 "application/vnd.openxmlformats-officedocument.wordprocessingml.document", // Word 2007+ ]; export const isFileTypeAllowed = (file) => { console.clear(); console.log("File Type: ", file); const isFileTypeSupported = ATTACHMENT_ALLOWED_FILE_TYPES.includes(file.type); return isFileTypeSupported; }; const uploadOptions = (questionId) => ({ name: "attachments", showUploadList: true, beforeUpload(file) { const isFileTypeSupported = isFileTypeAllowed(file); if (!isFileTypeSupported) { notification( NotificationTypes.ERROR, resources_EN.ERROR_LABEL, resources_EN.FILE_TYPE_ERROR, 3 ); return Upload.LIST_IGNORE; } else { const fileSizeLimit = isFileSizeValid(file); if (!fileSizeLimit) { notification( NotificationTypes.ERROR, resources_EN.ERROR_LABEL, resources_EN.FILE_SIZE_ERROR, 3 ); return Upload.LIST_IGNORE; } } customRequest({ file, questionId }); return false; }, onRemove: (file) => { handleFileRemove(file); }, });
<Form.Item name={[index, FORM_VALIDATION_KEYS.ATTACHMENT]} valuePropName="fileList" getValueFromEvent={normFile} > <Upload {...uploadOptions(question.questionId)}> <Button className="submitBtn" title={resources_EN.ADD_ATTACHMENTS} /> </Upload> </Form.Item>
原因分析
file.type是浏览器根据文件扩展名或文件头推断的MIME类型,但该值不具备可靠性:
- 不同操作系统、浏览器的MIME类型映射规则存在差异;
- 部分文件可能未正确关联系统MIME类型(比如自定义扩展名、文件被重命名但内容未变更);
- 特定环境下浏览器无法读取文件的MIME信息,直接返回空字符串。
解决方案
不要单纯依赖file.type,改用文件扩展名校验作为补充(或替代),通过双重校验确保准确性。
1. 定义允许的文件扩展名列表
export const ALLOWED_EXTENSIONS = [ 'zip', 'rar', '7z', 'jpeg', 'png', 'jpg', 'pdf', 'xls', 'xlsx', 'ppt', 'pptx', 'doc', 'docx' ];
2. 修改类型校验函数
同时校验file.type和文件扩展名,满足任一条件即判定为合法:
export const isFileTypeAllowed = (file) => { // MIME类型校验 const isMimeTypeValid = ATTACHMENT_ALLOWED_FILE_TYPES.includes(file.type); // 文件扩展名校验 const fileName = file.name.toLowerCase(); const fileExtension = fileName.slice(fileName.lastIndexOf('.') + 1); const isExtensionValid = ALLOWED_EXTENSIONS.includes(fileExtension); // 业务可根据需求调整为"&&"(同时满足)或"||"(满足其一) return isMimeTypeValid || isExtensionValid; };
3. 可选:增强校验(读取文件头)
如果担心用户通过修改扩展名绕过校验,可以读取文件前几个字节判断真实文件类型(以PDF为例):
// 判断是否为真实PDF文件(文件头标识为%PDF-) const isPdfFile = (file) => { return new Promise((resolve) => { const reader = new FileReader(); reader.onload = (e) => { const arr = new Uint8Array(e.target.result).subarray(0, 4); const header = arr.reduce((acc, byte) => acc + byte.toString(16), ''); resolve(header === '25504446'); // %PDF对应的十六进制值 }; reader.readAsArrayBuffer(file.slice(0, 4)); }); };
在beforeUpload中使用异步校验:
beforeUpload(file) { return Promise.all([ Promise.resolve(isFileTypeAllowed(file)), Promise.resolve(isFileSizeValid(file)), isPdfFile(file) // 仅针对PDF文件,其他类型可扩展对应判断逻辑 ]).then(([typeValid, sizeValid, pdfValid]) => { if (!typeValid || !sizeValid || !pdfValid) { notification(NotificationTypes.ERROR, resources_EN.ERROR_LABEL, resources_EN.FILE_TYPE_ERROR, 3); return Upload.LIST_IGNORE; } customRequest({ file, questionId }); return false; }); }
额外优化建议
在Upload组件上添加accept属性,前端层面限制文件选择框仅显示允许的类型,减少用户选择错误文件的概率:
<Upload {...uploadOptions(question.questionId)} accept=".zip,.rar,.jpeg,.png,.jpg,.pdf,.xls,.xlsx,.ppt,.pptx,.doc,.docx" > <Button className="submitBtn" title={resources_EN.ADD_ATTACHMENTS} /> </Upload>
注意:accept仅为前端限制,用户仍可手动选择所有文件,因此后端必须同步做相同的文件类型校验。
内容的提问来源于stack exchange,提问作者Abdullah Ishtiaq
相关产品推荐
相关产品推荐

