我有一个文件目录需要使用PHP批处理进行处理。这些文件通过FTP在服务器上复制。其中一些文件非常大,复制需要很长时间。如何在PHP中确定文件是否仍在传输(以便跳过该文件的处理并在批处理进程的下一次运行中处理它)?
一种可能性是获取文件大小,等待几秒钟,然后验证文件大小是否不同。但这并不是绝对可靠的,因为存在一定概率传输只是暂停了一段时间...
我有一个文件目录需要使用PHP批处理进行处理。这些文件通过FTP在服务器上复制。其中一些文件非常大,复制需要很长时间。如何在PHP中确定文件是否仍在传输(以便跳过该文件的处理并在批处理进程的下一次运行中处理它)?
一种可能性是获取文件大小,等待几秒钟,然后验证文件大小是否不同。但这并不是绝对可靠的,因为存在一定概率传输只是暂停了一段时间...
其中一种最安全的方法是使用临时名称上传文件,传输完成后再将其重命名。您的程序应跳过临时名称的文件(一个简单的扩展名就足够了)。显然,这需要客户端(上传者)的配合,因此并不理想。
[如果需要的话,这还允许您在一定时间段后删除失败的(部分)传输。]
基于轮询文件大小的任何方法都存在竞态条件和不安全性。
另一种方案(也需要上传者的合作)可能涉及首先上传文件的哈希值和大小,然后再上传实际文件。这使您可以知道传输何时完成以及传输是否一致。(围绕这个想法有很多变体。)
不需要客户端协作的方法是检查文件是否被其他进程打开。 (如何执行此操作取决于操作系统-我不知道有哪个PHP内置函数可以执行此操作。 on unix-type平台上可以使用lsof和/或fuser,在Windows上有相应的API。)如果另一个进程已经打开了该文件,则该文件很可能还不完整。
请注意,如果您允许重新启动/恢复上传,或者如果您的FTP服务器软件在整个传输期间不保持文件打开状态,则此最后一种方法可能不是绝对可靠的,因此请自行斟酌。
root
身份运行lsof
和fuser
,才能告诉您有关其他用户的信息。此外,如果您的文件位于NFS fs上,并且从不同于运行lsof
或fuser
命令的计算机正在写入该文件,则它们将不会告诉您任何内容。 - ejoubaud一些FTP服务器会在特定事件发生时运行命令。因此,如果你的FTP服务器允许这样做,那么你可以建立一个简单的信号方案来让你的应用程序知道文件已经被上传成功或者部分上传成功(“部分”是因为你不知道用户是否打算完全上传文件还是只上传部分)。这个信号方案可以非常简单,比如创建一个名为“uploaded_file_name.ext.complete”的文件,然后你将监视具有“.complete”扩展名的文件是否存在。
现在,你可以检查是否可以写入文件。如果文件正在上传,大多数FTP服务器是不允许你这样做的。
Mat 还提到了另一种方法,使用系统特定的技术来检查文件是否被其他进程打开。
最好的检查方法是使用flock在文件上获取独占锁。sftp/ftp进程将使用fopen库。
// try and get exclusive lock on file
$fp = fopen($pathname, "r+");
if (flock($fp, LOCK_EX)) { // acquire an exclusive lock
flock($fp, LOCK_UN); // release the lock
fclose($fp);
}
else {
error_log("Failed to get exclusive lock on $pathname. File may be still uploading.");
}
$result = false;
$tryies = 5;
if (file_exists($filepath)) {
for ($i=0; $i < $tryies; $i++) {
sleep(1);
$filesize[] = filesize($filepath);
}
$filesize = array_unique($filesize);
if (count($filesize) == 1) {
$result = true;
} else {
$result = false;
}
}
return $result;