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
相关产品推荐
相关产品推荐

