Node.js API调用video-stitch合并视频时报ffmpeg路径打开错误
问题原因分析
核心报错moov atom not found表示FFmpeg读取到的MP4文件不完整,MOOV元数据块通常位于MP4文件尾部,文件未完全写入、读取权限不足、路径错误都会触发该报错,结合你「单独运行正常、仅接口调用时出错」的特征,常见原因如下:
- 接口逻辑时序问题:如果你的接口是先接收用户上传的视频,收到文件后立即调用拼接逻辑,此时文件流还未完全写入磁盘,FFmpeg读取到的是半截文件,自然解析失败。本地测试时文件早已完成写入,所以不会触发该问题。
- 权限不匹配:本地测试时你用个人登录用户执行代码,默认对自己的目录有读写权限;如果API服务是通过PM2、系统服务等方式启动,运行账户可能没有
E:\\WS\\Projects\\vitok\\videos\\source\\目录的读取权限,导致FFmpeg无法打开文件。 - 动态路径错误:如果接口里的文件路径是动态生成的,可能存在Windows路径反斜杠未正确转义、路径拼接出错的问题,导致实际传入FFmpeg的路径和预期不符。
- 异步错误未捕获:你现有代码的外层
try/catch只能捕获同步异常,videoConcat.concat()返回的是Promise,异步抛出的错误不会被外层捕获,所以会触发UnhandledPromiseRejection警告。
解决步骤
- 先做基础校验:在接口调用拼接逻辑前,加入文件校验代码,确认文件状态正常:
const fs = require('fs/promises'); // 传入你要拼接的文件路径 const checkFile = async (filePath) => { await fs.access(filePath, fs.constants.R_OK); const stat = await fs.stat(filePath); console.log(`文件${filePath}大小:${stat.size}字节,可读`); } // 调用拼接前逐个校验文件 await Promise.all([ checkFile("E:\\WS\\Projects\\vitok\\videos\\source\\6999919482068077826.mp4"), checkFile("E:\\WS\\Projects\\vitok\\videos\\source\\7010852541999205637.mp4") ])
如果校验报错,就可以定位是权限或者路径的问题。
2. 修正异步处理逻辑:把拼接逻辑改成async/await写法,统一捕获异常:
const mergeVideo = async () => { try { const outputFileName = await videoConcat({ silent: false, }) .clips([ {fileName: "E:\\WS\\Projects\\vitok\\videos\\source\\6999919482068077826.mp4"}, {fileName: "E:\\WS\\Projects\\vitok\\videos\\source\\7010852541999205637.mp4"}, ]) .output("test.mp4") .concat() console.log(`输出文件:${outputFileName}`); } catch (err) { console.error('拼接失败', err); throw err; } }
- 如果是上传后立即拼接的场景,必须等待文件写入流的
finish/close事件触发后,再调用拼接逻辑,不要在收到文件请求时立即执行拼接。 - 排查API服务运行权限,给运行账户分配视频存储目录的读写权限,或者把测试目录换到公共目录(比如D盘根目录新建文件夹测试)排除权限影响。
内容的提问来源于stack exchange,提问作者Abdelhamied mostafa
相关产品推荐
相关产品推荐

