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

如何使用Express为MongoDB GridFsBucket文件生成直接下载链接

解决方案

问题原因

  1. res.download 方法仅支持传入本地文件系统路径,无法直接接收Buffer或流对象,你的场景不需要使用该方法,手动设置响应头即可触发浏览器下载行为。
  2. gridfsbucket.openDownloadStream() 返回的是Node.js可读流实例,不能直接通过Buffer.from转换,必须等待流完整读取完成才能生成Buffer,更推荐直接用流管道传输避免全量加载文件到内存。

方案1:流直接管道传输(推荐)

该方案完全不需要临时存储文件,也不需要将整个文件加载到内存,大文件场景下内存占用极低,符合你的需求,代码如下:

const { ObjectId } = require('mongodb');
const mongoose = require('mongoose');

router.get('/download-file', async (req, res) => {
  try {
    const fileId = req.query.fileId;
    // 校验文件ID合法性
    if (!ObjectId.isValid(fileId)) {
      return res.status(400).send('无效的文件ID');
    }
    const ObjectFileId = new ObjectId(fileId);
    
    // 初始化GridFS桶
    const gridfsbucket = new mongoose.mongo.GridFSBucket(mongoose.connection.db, {
      chunkSizeBytes: 1024,
      bucketName: 'filesBucket'
    });

    // 查询文件元信息
    const files = await gridfsbucket.find({ _id: ObjectFileId }).toArray();
    if (!files || files.length === 0) {
      return res.status(404).send('文件不存在');
    }
    const file = files[0];

    // 设置响应头,触发浏览器下载
    res.set({
      'Content-Type': file.contentType || 'application/octet-stream',
      'Content-Disposition': `attachment; filename="${encodeURIComponent(file.filename)}"`,
      'Content-Length': file.length
    });

    // 直接将GridFS下载流管道到响应对象
    const downloadStream = gridfsbucket.openDownloadStream(ObjectFileId);
    downloadStream.pipe(res);

    // 处理流异常
    downloadStream.on('error', (err) => {
      console.error('下载流错误:', err);
      res.status(500).send('文件下载失败');
    });
  } catch (err) {
    console.error('下载接口错误:', err);
    res.status(500).send('服务器内部错误');
  }
});

方案2:Buffer返回方案(适合需要预处理文件内容的场景)

如果你确实需要先拿到完整文件Buffer做内容处理,可以按如下方式修改代码:

封装的文件下载方法修改

// Node v15+ 内置stream/promises,低版本可使用concat-stream第三方包实现相同功能
const { buffer } = require('stream/promises');

async function downloadFileFromDB(fileId) {
  const gridfsbucket = new mongoose.mongo.GridFSBucket(mongoose.connection.db, {
    chunkSizeBytes: 1024,
    bucketName: 'filesBucket'
  });

  try {
    const stream = gridfsbucket.openDownloadStream(fileId);
    // 等待流读取完成转为Buffer
    const fileBuffer = await buffer(stream);
    // 查询文件元信息返回
    const files = await gridfsbucket.find({ _id: fileId }).toArray();
    return {
      buffer: fileBuffer,
      filename: files[0]?.filename || 'unknown',
      contentType: files[0]?.contentType || 'application/octet-stream'
    };
  } catch (err) {
    console.error('读取文件出错:', err);
    throw err;
  } 
}

路由代码修改

router.get('/download-file', async (req,res) => {
  try {
    const fileId = req.query.fileId;
    if (!ObjectId.isValid(fileId)) {
      return res.status(400).send('无效的文件ID');
    }
    const ObjectFileId = new ObjectId(fileId);
    const fileData = await fileFacade.downloadFileFromDB(ObjectFileId);
    
    // 设置响应头后返回Buffer
    res.set({
      'Content-Type': fileData.contentType,
      'Content-Disposition': `attachment; filename="${encodeURIComponent(fileData.filename)}"`,
      'Content-Length': fileData.buffer.length
    });
    res.send(fileData.buffer);
  } catch (err) {
    console.error('下载出错:', err);
    res.status(500).send('文件下载失败');
  }
})

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 19:09:03