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

React(JavaScript)开发中如何获取文件扩展名以正确下载文件

解决思路

核心问题是前端未获取到文件的真实类型和文件名信息,可通过以下两种方案解决,优先推荐第一种方案:

方案1:后端透传文件元信息(最稳妥)

S3存储文件时本身会携带ContentType等元数据,你也可以在上传文件时将原始文件名、扩展名等信息存入数据库的Training表中,后续在下载接口直接透传给前端即可。

后端代码修改:

routerTrainer.get("/download-training", async (req, res) => {
  const { training_id } = req.headers;
  const training = await Training.findOne({ where: { id: training_id } });

  if (!training) return res.status(404).send('资源不存在');

  // 1. 先读取S3文件的元信息(以aws-sdk为例,根据你封装的getTrainingS3调整即可)
  const s3Head = await s3.headObject({
    Bucket: '你的S3存储桶名称',
    Key: `${training.file_id}`
  }).promise();

  // 2. 优先用数据库中存储的原始文件名,没有的话可以自定义前缀+对应扩展名
  const fileName = training.original_file_name || `training_${training_id}`;

  // 3. 设置响应头,允许前端读取自定义头
  res.setHeader('Access-Control-Expose-Headers', 'x-file-name, Content-Type');
  res.setHeader('Content-Type', s3Head.ContentType);
  res.setHeader('x-file-name', encodeURIComponent(fileName));

  // 4. 返回文件流
  const readStream = getTrainingS3(`${training.file_id}`);
  readStream.on('error', () => res.status(500).send('文件读取失败'));
  readStream.on('close', () => res.end());
  readStream.pipe(res);
});

前端代码修改:

const downloadTraining = async (id) => {
  fetch(`${serverPath}/download-training`, {
    headers: { training_id: id },
  })
  .then(async (res) => {
    // 从响应头读取文件名
    const fileName = decodeURIComponent(res.headers.get('x-file-name'));
    const blob = await res.blob();
    const link = document.createElement("a");
    link.href = URL.createObjectURL(blob);
    link.setAttribute("download", fileName);
    document.body.appendChild(link);
    link.click();
    // 释放内存,避免性能问题
    URL.revokeObjectURL(link.href);
    link.remove();
  })
  .catch(err => console.error('下载失败', err));
};

方案2:前端根据Blob类型推断扩展名(无需改后端)

如果暂时不想调整后端代码,可以在前端维护MIME类型到扩展名的映射表,根据返回的Blob的type自动匹配扩展名:

// 可按需扩展更多类型
const mimeMap = {
  'application/pdf': 'pdf',
  'image/jpeg': 'jpg',
  'image/png': 'png',
  'image/gif': 'gif',
  'application/zip': 'zip',
  'application/msword': 'doc',
  'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'docx',
  'application/vnd.ms-excel': 'xls',
  'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'xlsx'
};

const downloadTraining = async (id) => {
  fetch(`${serverPath}/download-training`, {
    headers: { training_id: id },
  })
  .then((res) => res.blob())
  .then((blob) => {
    // 匹配扩展名,未知类型默认用bin
    const ext = mimeMap[blob.type] || 'bin';
    const link = document.createElement("a");
    link.href = URL.createObjectURL(blob);
    link.setAttribute("download", `training_${id}.${ext}`);
    document.body.appendChild(link);
    link.click();
    URL.revokeObjectURL(link.href);
    link.remove();
  });
};

注意事项

如果使用方案1,需要在后端的CORS配置中添加Access-Control-Expose-Headers允许x-file-name头暴露,否则前端无法读取到该自定义响应头。

内容的提问来源于stack exchange,提问作者Gonsa02

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 16:45:02