【发布时间】:2013-11-21 17:13:38
【问题描述】:
我目前有一个视频文件正在通过shell_exec() 调用转换为不同的格式。调用或格式转换没有问题,一切正常;但我的下一步是将该文件推送到 s3 存储桶。
但是,我注意到文件系统缓存不一定会立即刷新我新写入的文件,所以我将一个 0 字节文件推送到 s3,即使我在文件系统上查看它时也是正确的长度。在调用 shell_exec 和 s3-push 之间在我的代码中插入任意 5 秒睡眠解决了这个问题,但感觉非常糟糕,而且我无法知道 5 秒睡眠是否总是足够的,尤其是在使用更大的视频文件,系统正在加载中。
我很确定我不能强制刷新磁盘缓存,除非我执行同步调用(再次通过 shell_exec),但我不想使用这种方法,因为它会影响服务器上的所有文件任何缓冲数据,而不仅仅是我正在操作的单个文件。
所以我编写了这段简单的代码来监控文件大小,直到完成任何磁盘缓存刷新:
$prevSize = -1;
$size = filesize($myFileName);
while ($prevSize < $size) {
sleep(1);
clearstatcache(true, $myFileName);
if ($size > 0)
$prevSize = $size;
$size = filesize($myFileName);
}
基本上,只是循环直到至少有一些东西被刷新到文件中,并且文件大小已经保持一致至少一秒钟。
我不知道的是磁盘刷新是否只有在所有文件缓存都成功刷新到磁盘时才会更新大小;或者它是否会一次刷新几个块,我可能会发现自己试图将部分刷新的文件推送到 s3 并最终导致它被损坏。
任何建议将不胜感激。
编辑
现有代码如下所示:
private static function pushToS3($oldFilePath, $s3FileName, $newFilePath) {
self::testFileFlush($newFilePath);
file_put_contents(
$s3FileName,
file_get_contents($newFilePath)
);
}
private function processVidoe($oldFilePath, $s3FileName, $newFilePath) {
// Start Conversion
$command = "ffmpeg -i \"$oldFilePath\" -y -ar 44100 \"$newFilePath\"";
$processID = shell_exec("nohup ".$command." >/dev/null & echo $!");
self::pushToS3($oldFilePath, $s3FileName, $newFilePath);
unlink($newFilePath);
unlink($oldFilePath);
}
这是对在单个服务器上运行的旧遗留代码的修改,只是将文件存储在服务器的文件系统中;但是我已经更改了基础架构以在多个 AWS EC2 应用程序服务器上运行以实现弹性,并使用 S3 在 EC2 之间提供文件资源共享。文件由我们的用户上传到应用服务器,然后转换为 flv 并推送到 S3,以便所有 EC2 实例都可以使用它们。
长期解决方案将使用 AWS Elastic Transcoder,那时我可以简单地将原始数据推送到 S3 并向 Elastic Transcoder 提交排队请求,但这还需要一段时间。
【问题讨论】:
-
暂无评论,我忘了
&是隐含的;。您正在后台进行转换并捕获 PID,为什么不测试该 PID 是否仍在运行而不是与文件大小混在一起呢?if( shell_exec("ps -p $processID >/dev/null; echo $?") === "0" ) { echo "still running"; }就是说,为什么要让进程后台运行,然后等待它完成? -
目前,我需要等待它完成,因为我使用相同的代码块将结果文件推送到 S3 - 从长远来看,我几乎肯定会切换到 Elastic Transcoder 并将其视为排队作业或 gearman 任务(对于其他上传的文件,我与 LibreOffice 有类似的情况)......目前,我需要一个快速的解决方案(不是总是这样吗? )
-
但循环直到 ffmpeg 进程终止肯定比我不确定等到文件大小达到稳定时更好的解决方案
-
老实说,我会在我的回答中走这条路线,将第二个脚本分叉到后台来处理带有错误处理和后续 S3 上传的转码。 IMO 它更符合您使用 ETS/Gearman 的最终目标,不会让用户在转码发生时等待,并且可以完全了解 ffmpeg 返回错误/状态信息。