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

如何将formidable上传的文件直接存储到MongoDB GridFS

问题解答

不需要强制先把文件存到public/files本地目录再转存GridFS。你现在的代码之所以会把文件写到本地,是因为手动给formidable指定了业务目录作为上传临时路径,完全可以通过流对接的方式直接把上传文件写入GridFS,省去本地落盘、事后清理临时文件的步骤。

现有代码的可优化点

  • 你引入的gridfs-stream是已经停止维护的第三方旧包,当前MongoDB Node.js驱动原生自带GridFSBucket API,不需要额外依赖旧包,兼容性更好。
  • 代码里的File.create()是未定义的无效逻辑,没有实际写入GridFS的作用。
  • 将上传临时目录设为对外暴露的public/files存在安全风险,临时文件本来就不应该能被外部直接访问。
  • 每次请求都新建Mongo客户端连接会造成连接泄漏,建议全局复用连接实例。

推荐实现(直接流上传,不落本地业务盘)

通过formidable的文件事件直接把上传流对接GridFS的上传流,全程不会把完整文件写入你的项目目录,性能更好也没有临时文件清理负担:

import formidable from 'formidable';
import { MongoClient, GridFSBucket } from 'mongodb';

export const config = {
  api: {
    bodyParser: false,
  },
};

// 全局复用Mongo客户端,避免连接泄漏
let mongoClient;
async function getMongoClient() {
  if (!mongoClient) {
    mongoClient = new MongoClient(process.env.MONGODB_URI);
    await mongoClient.connect();
  }
  return mongoClient;
}

export default async (req, res) => {
  const client = await getMongoClient();
  const database = client.db('AdStitchr');
  // 初始化GridFS桶,对应原有uploads集合
  const bucket = new GridFSBucket(database, {
    bucketName: 'uploads'
  });

  const form = new formidable.IncomingForm();
  // 删除原有form.uploadDir = 'public/files'配置,使用系统默认临时目录即可
  form.keepExtensions = true;

  // 核心:监听文件开始上传事件,直接把流导向GridFS
  form.on('fileBegin', (formName, file) => {
    // 打开GridFS上传流,可自定义文件名和元数据
    const uploadStream = bucket.openUploadStream(file.newFilename, {
      chunkSizeBytes: 1048576,
      metadata: {
        originalName: file.originalFilename,
        mimetype: file.mimetype
      }
    });
    // 替换formidable默认的写本地逻辑,直接把上传数据写入GridFS
    file.writeStream = uploadStream;
    // 挂载生成的文件ID,方便后续返回
    file.gridFsId = uploadStream.id;
  });

  form.parse(req, (err, fields, files) => {
    if (err) {
      return res.status(500).send(err.message);
    }
    const uploadedFile = files.file;
    console.log('上传完成,GridFS文件ID:', uploadedFile.gridFsId);
    // 此处可添加自定义业务逻辑,比如把文件ID关联存储到业务表
    res.status(200).json({
      status: 'success',
      fileId: uploadedFile.gridFsId,
      fileName: uploadedFile.newFilename
    });
  });
};

临时文件转存方案(不推荐)

如果不想调整流逻辑,也可以保留临时文件转存的模式,但需要注意两点:

  • 不要把临时目录设到public/files这类对外可访问的路径,使用formidable默认的系统临时目录即可
  • 转存完成后必须手动删除临时文件,避免磁盘空间被占满
    核心转存代码片段如下:
import fs from 'fs/promises';
// form.parse回调内逻辑
form.parse(req, (err, fields, files) => {
  if (err) return res.status(500).send(err.message);
  const file = files.file;
  const readStream = fs.createReadStream(file.filepath);
  const uploadStream = bucket.openUploadStream(file.newFilename);
  
  readStream.pipe(uploadStream);
  uploadStream.on('finish', async () => {
    // 转存完成删除临时文件
    await fs.unlink(file.filepath);
    res.status(200).json({ status: 'success', fileId: uploadStream.id });
  });
  uploadStream.on('error', (uploadErr) => {
    res.status(500).send(uploadErr.message);
  });
});

这种方案比直接流上传多了一次本地磁盘读写,性能更差,还需要额外处理异常场景下的临时文件清理,优先使用前面的流对接方案。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 01:09:29