FFmpeg命令输出直接存储至AWS S3时文件大小为0B的问题
问题根源分析
你的核心问题是错误地把Laravel的PHP方法直接嵌入到Shell命令里。Storage::put('introfinal.mp4', '')是Laravel的PHP代码,执行FFmpeg的系统Shell根本无法解析这段代码,导致FFmpeg的输出目标完全无效,最终生成0B的空文件。
另外要注意:FFmpeg本身不能直接将输出写入AWS S3(S3不是本地文件系统),必须先在本地生成处理好的视频文件,再通过Laravel的Storage系统上传到S3。
修正方案
我们分五步来解决这个问题:
- 创建本地临时文件用于FFmpeg输出
- 构建安全的FFmpeg命令并执行
- 验证视频是否生成成功
- 将临时文件上传到S3
- 清理本地临时文件
以下是完整的修正代码:
// 1. 创建本地临时文件(避免文件名冲突) $tempFile = tempnam(sys_get_temp_dir(), 'ffmpeg_intro_'); // 也可以指定Laravel存储目录下的临时文件夹(需确保目录存在) // $tempDir = storage_path('temp'); // if (!file_exists($tempDir)) mkdir($tempDir, 0755, true); // $tempFile = $tempDir . '/introfinal_' . uniqid() . '.mp4'; // 2. 构建安全的FFmpeg命令(用escapeshellarg防止命令注入) $inputUrl = config('medialibrary.s3.domain') . '/listing_video/intro.mp4'; $fontPath = storage_path('assets/video/FutuMd.ttf'); $introText = $request->get('intro_text'); $ffmpegCommand = sprintf( 'ffmpeg -i %s -vf "drawtext=fontfile=%s:text=%s:x=300:y=450:fontsize=65:fontcolor=white:enable=\'between(t,2,5)\'" %s', escapeshellarg($inputUrl), escapeshellarg($fontPath), escapeshellarg($introText), escapeshellarg($tempFile) ); // 执行命令并捕获输出和返回状态 exec($ffmpegCommand, $output, $returnCode); // 3. 验证FFmpeg是否成功生成有效视频 if ($returnCode !== 0 || !file_exists($tempFile) || filesize($tempFile) === 0) { // 清理临时文件(如果存在) if (file_exists($tempFile)) unlink($tempFile); die("FFmpeg处理失败:" . implode("\n", $output)); } // 4. 将临时文件上传到AWS S3 try { // 使用putFileAs更高效(避免把整个文件读入内存) Storage::disk('s3')->putFileAs('', $tempFile, 'introfinal.mp4'); // 小文件也可以用put方法:Storage::disk('s3')->put('introfinal.mp4', file_get_contents($tempFile)); } catch (\Exception $e) { unlink($tempFile); die("上传S3失败:" . $e->getMessage()); } // 5. 清理本地临时文件,释放磁盘空间 unlink($tempFile); echo "视频处理并上传成功!";
关键注意事项
- 命令注入防护:必须用
escapeshellarg()转义所有动态参数(比如用户输入的intro_text),防止恶意输入破坏命令结构。 - 临时文件管理:操作完成后一定要删除临时文件,避免占用服务器磁盘空间。
- 错误处理:添加返回码和文件大小检查,确保FFmpeg确实生成了有效视频再上传,避免上传空文件到S3。
内容的提问来源于stack exchange,提问作者Jinal Somaiya
相关产品推荐
相关产品推荐

