node-fluent-ffmpeg转MP3无end/finish事件触发且上传失败
问题
使用ytdl-core下载视频后,通过node-fluent-ffmpeg转换为MP3时遇到两个问题:
- 上传Google Cloud Storage的代码在转换完成前就执行,抛出文件不存在的
ENOENT错误 - ffmpeg的
end/finish事件均未触发,且报错退出码1,提示「Output file #0 does not contain any stream」
代码示例
ytdl(url?.toString() as string) .once('data', (data) => { console.log('start', data); }) .on('progress', async function (progress, downloaded, total) { console.log(parseInt(((downloaded / total) * 100).toString()) + '%'); }) .on('finish', async function () { console.log('Download finished...'); console.log('using ffmpeg to convert into mp3'); Ffmpeg({ source: `/Users/user/project/server/output/${videoId}.mp4` }) .setFfmpegPath(ffmpeg.path) .toFormat('mp3') .saveToFile(`/Users/user/project/server/output/${videoId}.mp3`) .on('finish', () => { console.log('finished conversion'); }); console.log('after ffmpeg mp3 conversion'); const storage = new Storage({ keyFilename: `./key.json`, }); const bucketName = 'bucketname'; const bucket = storage.bucket(bucketName); let mp3_url; console.log( fs.existsSync(`/Users/user/project/server/output/${videoId}.mp3`) ); // returns false bucket.upload( `/Users/user/project/server/output/${videoId}.mp3`, // Error uploading: Error: ENOENT: no such file or directory, open '/Users/me/project/server/output/RMvenf7E-Dg.mp3' { destination: `${userId}/${videoId}.mp3`, }, function (err, file) { if (err) { console.error(`Error uploading: ${err}`); } else { console.log(`mp3 uploaded to ${bucketName}.`); console.log(file?.publicUrl()); } } ); .pipe( fs.createWriteStream(`/Users/me/project/server/output/${videoId}.mp4`) ); console.log('after download and conversion'); req.on('close', async () => { res.end(); });
错误信息
Error uploading: Error: ENOENT: no such file or directory, open '/Users/polo/makeklips/server/output/RMvenf7E-Dg.mp3' [1] /Users/me/project/server/node_modules/.pnpm/fluent-ffmpeg@2.1.2/node_modules/fluent-ffmpeg/lib/processor.js:182 [1] handleExit(new Error('ffmpeg exited with code ' + code)); [1] ^ [1] Error: ffmpeg exited with code 1: Output #0, mp3, to '/Users/me/project/server/output/RMvenf7E-Dg.mp3': [1] Output file #0 does not contain any stream
解决方案
问题根源分析
- 路径不一致:ffmpeg读取的源视频路径是
/Users/user/...,但实际写入的路径是/Users/me/...,导致ffmpeg找不到源文件,转换失败后输出文件无流。 - 异步流程错误:
ytdl的finish事件仅表示下载完成,但通过pipe写入磁盘的流可能还未完成,此时读取文件会导致内容不完整或文件不存在。- 上传代码未等待ffmpeg转换完成就执行,自然找不到未生成的MP3文件。
修正步骤及代码
- 统一文件路径:确保源视频和输出MP3的路径一致,避免路径错误。
- 监听正确的流事件:监听
writeStream的finish事件,确保视频文件完全写入磁盘后再启动转换。 - 调整异步执行顺序:将GCS上传逻辑放到ffmpeg的
finish事件内,同时添加error事件监听处理转换失败的情况。
修正后的代码:
const videoOutputPath = `/Users/me/project/server/output/${videoId}.mp4`; const mp3OutputPath = `/Users/me/project/server/output/${videoId}.mp3`; const writeStream = fs.createWriteStream(videoOutputPath); ytdl(url?.toString() as string) .once('data', (data) => { console.log('start', data); }) .on('progress', (progress, downloaded, total) => { console.log(parseInt(((downloaded / total) * 100).toString()) + '%'); }) .pipe(writeStream); // 监听写入流的finish事件,确保视频完全写入磁盘 writeStream.on('finish', async () => { console.log('Download finished...'); console.log('using ffmpeg to convert into mp3'); Ffmpeg({ source: videoOutputPath }) .setFfmpegPath(ffmpeg.path) .toFormat('mp3') .saveToFile(mp3OutputPath) .on('finish', async () => { console.log('finished conversion'); // 转换完成后执行GCS上传 const storage = new Storage({ keyFilename: `./key.json`, }); const bucketName = 'bucketname'; const bucket = storage.bucket(bucketName); console.log(fs.existsSync(mp3OutputPath)); // 现在会返回true bucket.upload( mp3OutputPath, { destination: `${userId}/${videoId}.mp3`, }, function (err, file) { if (err) { console.error(`Error uploading: ${err}`); } else { console.log(`mp3 uploaded to ${bucketName}.`); console.log(file?.publicUrl()); } } ); }) .on('error', (err) => { console.error('ffmpeg转换失败:', err); }); }); console.log('开始下载和转换流程'); req.on('close', async () => { res.end(); });
关键修改说明
- 提取路径为变量,避免硬编码导致的路径不一致问题。
- 将
ytdl的finish事件替换为writeStream的finish事件,确保视频文件完全写入磁盘后再启动转换。 - 将GCS上传逻辑移到ffmpeg的
finish事件回调内,保证转换完成后再执行上传。 - 添加ffmpeg的
error事件监听,便于排查转换失败问题。
内容的提问来源于stack exchange,提问作者hey
相关产品推荐
相关产品推荐

