如何确定文件是否正在通过FTP传输

27

我有一个文件目录需要使用PHP批处理进行处理。这些文件通过FTP在服务器上复制。其中一些文件非常大,复制需要很长时间。如何在PHP中确定文件是否仍在传输(以便跳过该文件的处理并在批处理进程的下一次运行中处理它)?

一种可能性是获取文件大小,等待几秒钟,然后验证文件大小是否不同。但这并不是绝对可靠的,因为存在一定概率传输只是暂停了一段时间...


1
嗯,确实是个有趣的问题!+1 - Pekka
您可以使用ftp_nb_put,请参考文档中的while-loop示例。 - y_a_v_a
1
使用类似于“lsof | grep 文件名”的exec命令在Linux上进行检查。 - Haim Evgi
2
所有的尝试都可能失败,因为甚至FTP服务器也不知道文件何时完成:http://stackoverflow.com/questions/822528/during-a-ftp-upload-the-total-size-of-the-file-is-send/822605#822605 - 因此连接关闭将关闭文件,服务器不知道它是否已经完成。也许客户端只是想在重新连接后恢复上传。 - hakre
5个回答

11

其中一种最安全的方法是使用临时名称上传文件,传输完成后再将其重命名。您的程序应跳过临时名称的文件(一个简单的扩展名就足够了)。显然,这需要客户端(上传者)的配合,因此并不理想。

[如果需要的话,这还允许您在一定时间段后删除失败的(部分)传输。]

基于轮询文件大小的任何方法都存在竞态条件和不安全性。

另一种方案(也需要上传者的合作)可能涉及首先上传文件的哈希值和大小,然后再上传实际文件。这使您可以知道传输何时完成以及传输是否一致。(围绕这个想法有很多变体。)

不需要客户端协作的方法是检查文件是否被其他进程打开。 (如何执行此操作取决于操作系统-我不知道有哪个PHP内置函数可以执行此操作。 on unix-type平台上可以使用lsof和/或fuser,在Windows上有相应的API。)如果另一个进程已经打开了该文件,则该文件很可能还不完整。

请注意,如果您允许重新启动/恢复上传,或者如果您的FTP服务器软件在整个传输期间不保持文件打开状态,则此最后一种方法可能不是绝对可靠的,因此请自行斟酌。


1
该传输是由用户通过ftp进行的,而不是由php发起的。我不希望用户在文件传输完成后需要重新命名文件。 - murze
1
编辑了我的答案,提供了一种不需要客户端任何东西的方法。这并不是绝对可靠的。(我认为使用普通FTP没有什么是绝对可靠的。) - Mat
1
请注意:需要以root身份运行lsoffuser,才能告诉您有关其他用户的信息。此外,如果您的文件位于NFS fs上,并且从不同于运行lsoffuser命令的计算机正在写入该文件,则它们将不会告诉您任何内容。 - ejoubaud

5
我们的服务器管理员建议使用ftpwho,它可以输出当前正在传输的文件。因此,解决方案是解析ftpwho的输出,以查看目录中是否正在传输文件。
链接:http://www.castaglia.org/proftpd/doc/ftpwho.html

4

一些FTP服务器会在特定事件发生时运行命令。因此,如果你的FTP服务器允许这样做,那么你可以建立一个简单的信号方案来让你的应用程序知道文件已经被上传成功或者部分上传成功(“部分”是因为你不知道用户是否打算完全上传文件还是只上传部分)。这个信号方案可以非常简单,比如创建一个名为“uploaded_file_name.ext.complete”的文件,然后你将监视具有“.complete”扩展名的文件是否存在。

现在,你可以检查是否可以写入文件。如果文件正在上传,大多数FTP服务器是不允许你这样做的。

Mat 还提到了另一种方法,使用系统特定的技术来检查文件是否被其他进程打开。


3

最好的检查方法是使用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.");
}

大多数文件都会触发else语句,即使它们没有上传。 - Rapti
FTP服务器不会锁定正在传输的文件。因此,这个测试是错误的。 - artoodetoo
对我来说看起来没问题...如果你无法获取锁定...文件仍在上传中...似乎是他正在寻找的...为此点赞。 - james walker

1
这并不是一种很好的技巧,但它很简单 :-),您也可以使用filemtime做同样的事情。
$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;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接