我目前有一个视频文件正在通过shell_exec()
调用转换为不同的格式。调用或格式转换没有问题,一切正常;但我的下一步是将该文件推送到s3存储桶中。
然而,我注意到文件系统缓存不一定会立即刷新我新写的文件,所以我将一个0字节的文件推送到s3,尽管每当我在文件系统上查看它时,它的长度都是正确的。在对shell_exec的调用和s3推送之间,在我的代码中插入一个任意的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提交排队请求,但这还需要一段时间。
除非您正在执行以下操作之一,否则您所描述的行为应该是不可能的:
- 将数据写入临时文件,然后将其复制/移动到要上载的位置
- 用两台不同的机器装载同一个分区,一台正在写入文件,另一台正在尝试上载文件
- 某种黑客软件缓冲正在发生
否则,FS缓存应该对操作系统上运行的任何东西都是完全透明的,并且操作系统将从缓存中为任何尚未写入磁盘的缓存数据请求提供服务。
在#2的情况下,通过将缓存方法更改为直写而不是回写,您应该能够在一定程度上绕过。您的写入性能会下降,但数据总是立即写入,因此数据丢失的风险要小得多。
编辑
Ffmpeg可能正在触摸您给它的文件名,使用临时文件转换视频,然后将完成的文件移动到目的地。我假设启动转换的脚本会对过程进行背景处理,因为否则就不会对完成的文件是否存在产生任何混淆。
我的建议是,与其只将ffmpeg分叉到后台进程中,然后测试是否存在结束文件,不如在后台将其分叉到另一个PHP脚本中,在该脚本中,您可以调用ffmpeg而不必对其进行备份,然后在上传完成后触发上传。
例如:
//user-facing.php
<?php
echo "Queueing your file for processing..."
shell_exec("/usr/bin/php /path/to/process.php /path/to/source.mpg /path/to/dest.mpg &")
echo "Done!"
和:
//process.php
<?php
exec(sprintf("/path/to/ffmpeg -options %s %s", $argv[1], $argv[2]), $output, $exit_code);
if($exit_code === 0) {
upload_to_s3($argv[2]);
} else {
//notify someone of the error
}
这还可以让您捕获ffmpeg的输出和返回代码,并对其采取行动,而不是想知道为什么有些视频只是无声地无法转换。
您在这里看到的不是磁盘缓存的影响。磁盘缓存是透明的——它的行为对用户来说是不可见的。
您在这里看到的是一个应用程序的行为,它创建了一个文件,但不会立即写入。你需要找到其他方法来检查它是否完成了。