PHP - 如何在不支持SIZE的FTP服务器上检查文件是否存在

8

是的,我知道,很难相信那些FTP服务器仍然存在,但它们确实存在。IBM iSeries机器运行这样的服务器。

我已经得到了一个答案,其中涉及ftp_nlistin_array,但是,正如你们中的一些人可能已经猜到的那样,当目录包含大量项目时,这是慢的

由于不支持SIZE,所以只要使用读取模式(请记住,FTP包装器不支持x),fopen总是失败,而ftp_size总是返回-1(这是预期的),file_exists总是返回false(也许是因为它在内部使用SIZE?)。

  • ftp_getftp_fget可以解决问题,但如果文件存在,则会下载整个文件。并不好。一种可能的解决方案涉及使用仅以读模式打开的文件的处理程序传递ftp_fget,并捕获引发的警告。当文件不存在时情况不同,但是这个解决方案感觉粗糙,并且我真的不知道它是否可行(也许有人可以给出一个例子)。

  • 另一种解决方案使用ftp_nb_get/ftp_nb_fget尝试检索文件。如果函数返回0FTP_FAILED),则文件可能不存在。我仍然需要处理一个临时本地文件,如果返回FTP_MOREDATA(否则其他FTP命令无法发出),就会关闭并重新打开连接。

你有任何想法吗?


使用 ftp_nlist 时,它实际上有多慢? - silkfire
1
我会尝试这个技巧:使用 ftp_rename($connection, $testedFile, 'someThingThatDoesntExists'); 如果失败,那么我们就有了答案(_'NO')。但这取决于目标是否可写且我们有重命名的权限。如果成功,那么就将其改回来,同时我们也有了答案('YES'_)。我没有测试过这个方法,但你可以尝试并告诉我会发生什么。 - Alma Do
@silkfire 对于5000多个文件,可能需要一分钟的时间,但即使几秒钟对于这样的任务来说也太长了。 - MaxArt
@silkfire,5407个文件花费了43.76秒。由于某种服务器缓存的原因,以下基准测试仅需大约0.4秒,但我不能依靠它。 - MaxArt
@AlmaDo 看起来有一个更简单的方法:我可以使用 ftp_rename 将文件重命名为自身。既然使用 ftp_rename 基本上是你的想法,如果你把所有东西都放在一个答案中,我就可以接受它。 - MaxArt
显示剩余15条评论
3个回答

6
SIZE命令不是必需的。您可以使用函数ftp_nlist()来代替,因为FTP LIST命令允许将目录及文件作为参数传递。尽管PHP文档未提到它在RFC 959(第32页)中有说明并且可以正常工作。以下是一个示例(感谢Debian!)。
$server = 'ftp.us.debian.org';
$port   =  21; 
$user   = 'anonymous';
$pwd    = 'foo@bar.xxx';

$conn = ftp_connect($server);
$ret = ftp_login($conn, $user, $pwd);

foreach(array(
    'debian/README.html',
    'NOT_FOUND.html'
) as $file) {
    $listing = ftp_nlist($conn, $file);
    if(empty($listing)) {
        echo "$file was not found on $server\n";
    } else {
        echo "$file was found on $server\n";
    }
}

或者,表示为函数形式:
function ftp_file_exists(
    $server,
    $filename,
    $user = 'anonymous' ,
    $pwd = '',
    $port = 21
) {
    $conn = @ftp_connect($server);
    if($conn === FALSE) {
        die("Failed to connect to $server");
    }

    $ret = @ftp_login($conn, $user, $pwd);
    if($ret === FALSE) {
        die("Failed to login at $server");
    }

    $listing = @ftp_nlist($conn, $file);
    if($listing === FALSE) {
        die("Failed to obtain LIST response from $server");
    }

    return !empty($listing);
}

在评论区出现了一些有关LIST结果的讨论,我们需要进一步说明其实用性和可靠性。

在服务器上创建文件

请注意,您应该避免依赖类似以下的内容:

if(file_not_exists_on_server($filename)) {
    create_file_on_server($filename);
}

因为第一次和第二次函数之间可能会由另一个客户端创建该文件。虽然这在本地文件系统上也是正确的,但在分布式客户端服务器应用程序中更容易发生,因为响应时间比本地文件系统长,并且可能存在许多甚至匿名的客户端(如上例所示)。
当远程创建文件时,建议在公共可写文件夹中遵循强命名方案以避免冲突。如果遵循此方案,则只需编写并不必担心。最糟糕的情况是您意外覆盖了其他人意外创建的内容。但是,谁会像“ / client / id / file_name.txt”这样意外创建东西呢?
从服务器下载文件或在服务器上移动、删除文件时,请勿关注操作前文件是否存在。只要做就可以了。但如果操作失败,则需要适当处理错误。

我将接受这个答案,因为它不涉及文件操作。我已经在我的情况下测试过它,它可以工作。 - MaxArt
@MaxArt 谢谢 :) 我必须承认,你的问题是一个好问题,解决它很有趣。最后,基本上是因为 PHP 手册缺乏文档。此外,我必须说,长时间以来,我理解你的问题是“如何确保文件存在”,但你没有这样说。不过,我已经添加了一些关于这个问题的句子。(也许我太专注于这个细节,因为这在我的前一个项目中被指出为一个问题)。如果你可以编辑你的问题(只需添加一个空格或其他任何内容,但 SO 需要),那么我会点赞它。 - hek2mgl

2

您可以使用某种 "小技巧":尝试对已测试的文件执行 ftp_rename()。示例代码如下:

$result = ftp_rename($connection, $testedFile, $testedFile);

如果失败,那么我们很可能是在处理不存在的文件。除非你确定是权限问题,否则不能百分之百肯定。但如果成功了,那么该文件肯定存在。我怀疑没有其他方法可以使用并允许您避免将文件传输到本地文件系统或处理完整目录列表。


1
谢谢,希望这能帮助到其他处于同样情况的开发者。 - MaxArt
这并不准确。但它会在检查的那一刻正确回答。它既与并发连接无关,也与可能在其后的某些代码无关。 - Alma Do
你需要再读一遍并告诉我为什么 ftp_nlist($conn_id, $filename) 不适合在这里。你能说出一个原因吗? - hek2mgl
如果这个速度真的太慢了,可以使用纯LIST。它支持文件名和目录。 - hek2mgl
2
根据您对“仅在检查期间”存在的疑虑,ftp_nlist将如何帮助?它还将显示调用时的情况。所以 - 是的,文件以后可以被删除/移动/或/其他操作。那么有什么区别呢? - Alma Do
显示剩余10条评论

0

正如我在问题中明确说明的那样,file_exists 不起作用。Stat 函数也不起作用(出现“stat 失败”错误),而 is_file 总是返回 false - MaxArt

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