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

NestJS从GridFS返回文件时Content-Type不生效问题求助

解决Nest控制器从GridFS返回Zip文件时Content-Type不生效及下载触发问题

我来帮你搞定这个问题——我之前也碰到过类似的场景,当你用@Res()直接接管响应对象时,Nest的@Header()装饰器经常会被忽略,因为此时你已经手动控制了响应流的处理。另外,要让浏览器识别这是个需要下载的Zip文件,光设置Content-Type还不够,还得加上Content-Disposition头来明确告诉浏览器该怎么做。

核心修改思路

  • 放弃使用@Header()装饰器,改用手动调用res.setHeader()设置响应头(因为@Res()会绕过Nest的自动响应处理)
  • 添加Content-Disposition头,指定attachment模式和文件名,强制浏览器触发下载行为
  • 增加错误处理,避免流异常时响应挂起

修改后的控制器代码

import { Response } from 'express';

@Get(':owner/:name/v/:version/download')
async downloadByVersion(
  @Param('owner') owner: string,
  @Param('name') name: string,
  @Param('version') version: string,
  @Res() res: Response,
): Promise<void> {
  try {
    const bundleData = await this.service.getSwimbundleByVersion(owner, name, version);
    const downloadFile = await this.service.downloadSwimbundle(bundleData['meta']['fileData']['_id']);

    // 手动设置Content-Type,替换原来的@Header()装饰器
    res.setHeader('Content-Type', 'application/zip');
    // 添加Content-Disposition头,触发浏览器下载,指定文件名
    res.setHeader(
      'Content-Disposition',
      `attachment; filename="${name}_v${version}.zip"`
    );

    // 将GridFS流pipe到响应对象,完成文件传输
    downloadFile.pipe(res);
  } catch (error) {
    // 处理异常,返回错误响应
    res.status(500).send('Failed to download the bundle file');
  }
}

额外优化建议(可选)

如果你的GridFS文件元数据中已经存储了正确的contentType,可以从文件信息中读取这个值,让代码更灵活:

修改Service层代码

async downloadSwimbundle(fileId: string): Promise<{ stream: GridFSBucketReadStream; contentType: string }> {
  const db = await this.dbSource.db;
  const bucket = new GridFSBucket(db, { bucketName: this.collectionName });
  
  // 先查询文件的元数据,获取存储的contentType
  const fileMeta = await bucket.find({ _id: new ObjectID(fileId) }).next();
  const downloadStream = bucket.openDownloadStream(new ObjectID(fileId));

  // 返回流和对应的contentType,默认fallback到application/zip
  return {
    stream: downloadStream,
    contentType: fileMeta?.contentType || 'application/zip'
  };
}

对应修改控制器

const { stream: downloadFile, contentType } = await this.service.downloadSwimbundle(
  bundleData['meta']['fileData']['_id']
);
res.setHeader('Content-Type', contentType);

这样不管你存储的是Zip还是其他类型的文件,都能自动匹配正确的Content-Type啦。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 09:23:27