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

如何在使用Multer和GridFS上传视频至MongoDB前裁剪至3分钟?

要实现上传前裁剪视频再存入MongoDB的GridFS,核心是先让Multer接收文件到临时存储(磁盘或内存),用ffmpeg完成裁剪后,再将处理后的文件上传到GridFS,最后清理临时文件。你选ffmpeg是完全正确的,对应的fluent-ffmpeg包是Node.js里处理音视频的主流工具,接下来给你梳理具体的修正方案:

1. 依赖准备

确保安装必要的包,且系统环境中存在ffmpeg(fluent-ffmpeg依赖本地ffmpeg):

npm install multer fluent-ffmpeg mongodb mongoose

Windows/Mac需单独安装ffmpeg并配置到环境变量,Linux可通过包管理器安装(如apt install ffmpeg)。

2. 修正代码逻辑

你的现有代码存在几个问题:

  • 用req.file.filename传入ffmpeg错误,应使用req.file.path(完整临时文件路径)
  • 输出格式同时设置.format('avi')和文件名后缀.mp4,格式冲突
  • 处理完视频后未将裁剪后的文件上传到GridFS,直接返回了原文件
  • 错误处理顺序不合理,应先判断Multer的上传错误,再执行ffmpeg处理

以下是包含GridFS上传逻辑的完整修正代码:

const multer = require('multer');
const ffmpeg = require('fluent-ffmpeg');
const mongoose = require('mongoose');
const Grid = require('gridfs-stream');
const fs = require('fs');
const path = require('path');

// 连接MongoDB
mongoose.connect('mongodb://localhost:27017/your-db-name');
const db = mongoose.connection;

// 初始化GridFS
let gfs;
db.once('open', () => {
  gfs = Grid(db.db, mongoose.mongo);
  gfs.collection('videos'); // 指定存储视频的集合名
});

// Multer配置:使用临时磁盘存储,避免内存溢出
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, './temp-uploads/'); // 需提前创建临时目录
  },
  filename: (req, file, cb) => {
    cb(null, Date.now() + '-' + file.originalname); // 生成唯一临时文件名
  }
});
const upload = multer({ storage: storage }).single('demo_file');

app.get('/', (req, res) => {
  res.send('hello world');
});

app.post("/upload-file", (req, res) => {
  upload(req, res, async (err) => {
    if (err) {
      return res.status(400).send("文件上传失败:" + err.message);
    }

    const tempInputPath = req.file.path;
    const tempOutputPath = `./temp-uploads/${Date.now()}-cropped.mp4`;

    try {
      // 用ffmpeg裁剪视频为3分钟
      await new Promise((resolve, reject) => {
        ffmpeg(tempInputPath)
          .addOptions([
            '-crf 23',
            '-preset veryslow',
            '-t 180', // 裁剪为180秒(3分钟)
            '-c:a aac',
            '-b:a 480k',
            '-b:v 480k'
          ])
          .aspect('16:9')
          .size('50%')
          .fps(30)
          .audioCodec('aac')
          .audioChannels(2)
          .format('mp4')
          .on('error', (e) => {
            reject(new Error("视频处理失败:" + e.message));
          })
          .on('end', resolve)
          .save(tempOutputPath);
      });

      // 将裁剪后的文件上传到GridFS
      const readStream = fs.createReadStream(tempOutputPath);
      const writeStream = gfs.createWriteStream({
        filename: `cropped-${req.file.originalname}`,
        contentType: req.file.mimetype
      });

      readStream.pipe(writeStream);

      writeStream.on('close', (file) => {
        // 清理临时文件
        fs.unlinkSync(tempInputPath);
        fs.unlinkSync(tempOutputPath);
        res.status(200).send({
          message: "视频裁剪并上传成功",
          fileId: file._id
        });
      });

      writeStream.on('error', (e) => {
        fs.unlinkSync(tempInputPath);
        fs.unlinkSync(tempOutputPath);
        res.status(500).send("GridFS上传失败:" + e.message);
      });

    } catch (e) {
      // 处理出错时清理临时文件
      fs.unlinkSync(tempInputPath);
      if (fs.existsSync(tempOutputPath)) {
        fs.unlinkSync(tempOutputPath);
      }
      res.status(500).send(e.message);
    }
  });
});

3. 关键注意点

  • 临时目录:需提前创建temp-uploads目录,避免Multer报错
  • 大文件处理:处理超大视频时,优先用磁盘存储而非内存存储(multer的memoryStorage),防止内存溢出
  • 裁剪规则:-t 180是从视频开头裁剪3分钟,若需从指定时间点开始裁剪,可改用-ss 起始秒数 -t 180
  • 错误处理:所有可能出错的环节都要清理临时文件,避免磁盘空间浪费
  • GridFS集合:可根据需求调整gfs.collection('videos')中的集合名

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 10:02:24