如何在使用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
相关产品推荐
相关产品推荐

