You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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类型,但该值不具备可靠性:

  1. 不同操作系统、浏览器的MIME类型映射规则存在差异;
  2. 部分文件可能未正确关联系统MIME类型(比如自定义扩展名、文件被重命名但内容未变更);
  3. 特定环境下浏览器无法读取文件的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 03:42:37