PHP FTP递归目录列表


PHP FTP recursive directory listing

我正在尝试制作一个递归函数,以获取数组中ftp服务器中的所有目录和子目录。

我尝试了很多我在网上找到的功能。最适合我的是这个:

public function getAllSubDirFiles() {
    $dir = array(".");
    $a = count($dir);
    $i = 0;
    $depth = 20;
    $b = 0;
    while (($a != $b) && ($i < $depth)) {
        $i++;
        $a = count($dir);
        foreach ($dir as $d) {
            $ftp_dir = $d . "/";
            $newdir = ftp_nlist($this->connectionId, $ftp_dir);
            foreach ($newdir as $key => $x) {
                if ((strpos($x, ".")) || (strpos($x, ".") === 0)) {
                    unset($newdir[$key]);
                } elseif (!in_array($x, $dir)) {
                    $dir[] = $x;
                }
            }
        }
        $b = count($dir);
    }
    return $dir ;
}

这个函数的问题是,它不允许目录的名称中有".",并且位于根目录中的每个文件也将被视为一个目录。所以我调整了功能,得到了这个:

public function getAllSubDirFiles($ip, $id, $pw) {
    $dir = array(".");
    $a = count($dir);
    $i = 0;
    $depth = 20;
    $b =0;
    while (($a != $b) && ($i < $depth)) {
        $i++;
        $a = count($dir);
        foreach ($dir as $d) {
            $ftp_dir = $d . "/";
            $newdir = ftp_nlist($this->connectionId, $ftp_dir);
            foreach ($newdir as $key => $x) {
                if (!is_dir('ftp://'.$id.':'.$pw.'@'.$ip.'/'.$x)) {
                    unset($newdir[$key]);
                } elseif (!in_array($x, $dir)) {
                    $dir[] = $x;
                }
            }
        }
        $b = count($dir);
    }
    return $dir ;
}

这工作得很好,但给出了我想要的结果。但它太慢了,无法使用。

我也尝试过使用ftp_rawlist,但它也有同样的缺点,速度非常慢。

public function getAllSubDirFiles() {
    $dir = array(".");
    $a = count($dir);
    $i = 0;
    $depth = 20;
    $b = 0;
    while (($a != $b) && ($i < $depth)) {
        $i++;
        $a = count($dir);
        foreach ($dir as $d) {
            $ftp_dir = $d . "/";
            $newdir = $this->getFtp_rawlist('/' . $ftp_dir);
            foreach ($newdir as $key => $x) {
                $firstChar = substr($newdir[$key][0], 0, 1);
                $a = 8;
                    while ($a < count($newdir[$key])) {
                        if ($a == 8) {
                            $fileName = $ftp_dir . '/' . $newdir[$key][$a];
                        } else {
                            $fileName = $fileName . ' ' . $newdir[$key][$a];
                        }
                        $a++;
                    }
                if ($firstChar != 'd') {
                    unset($newdir[$key]);
                } elseif (!in_array($fileName, $dir)) {
                    $dir[] = $fileName;
                }
            }
        }
        $b = count($dir);
    }
    return $dir;
}
public function getFtp_rawlist($dir) {
    $newArr = array();
    $arr = ftp_rawlist($this->connectionId, $dir);
    foreach ($arr as $value) {
        $stringArr = explode(" ", $value);
        $newArr[] = array_values(array_filter($stringArr));
    }
    return $newArr;
}

最近几天我一直被这个问题困扰着,我越来越绝望了。如果有人有任何建议,请告诉我

如果您的服务器支持MLSD命令,并且您有PHP 7.2或更新版本,则可以使用ftp_mlsd函数:

function ftp_mlsd_recursive($ftp_stream, $directory)
{
    $result = [];
    $files = ftp_mlsd($ftp_stream, $directory);
    if ($files === false)
    {
        die("Cannot list $directory");
    }
    foreach ($files as $file)
    { 
        $name = $file["name"];
        $filepath = $directory . "/" . $name;
        if (($file["type"] == "cdir") || ($file["type"] == "pdir"))
        {
            // noop
        }
        else if ($file["type"] == "dir")
        {
            $result = array_merge($result, ftp_mlsd_recursive($ftp_stream, $filepath));
        }
        else
        {
            $result[] = $filepath;
        }
    } 
    return $result;
} 

如果您没有PHP7.2,您可以尝试自己实现MLSD命令。首先,请参阅ftp_rawlist命令的用户注释:
https://www.php.net/manual/en/function.ftp-rawlist.php#101071


如果不能使用MLSD,则在判断条目是文件还是文件夹时会遇到特别的问题。虽然可以使用ftp_size技巧,但为每个条目调用ftp_size可能需要很长时间。

但是,如果您只需要对一个特定的FTP服务器进行操作,则可以使用ftp_rawlist以特定于平台的格式检索文件列表并对其进行解析。

以下代码采用通用的*nix格式。

function ftp_nlst_recursive($ftp_stream, $directory)
{
    $result = [];
    $lines = ftp_rawlist($ftp_stream, $directory);
    if ($lines === false)
    {
        die("Cannot list $directory");
    }
    foreach ($lines as $line)
    {
        $tokens = preg_split("/'s+/", $line, 9);
        $name = $tokens[8];
        $type = $tokens[0][0];
        $filepath = $directory . "/" . $name;
        if ($type == 'd')
        {
            $result = array_merge($result, ftp_nlst_recursive($ftp_stream, $filepath));
        }
        else
        {
            $result[] = $filepath;
        }
    }
    return $result;
}

有关DOS格式,请参阅:使用PHP从FTP获取目录结构。

我已经构建了一个OOP FTP客户端库,它可以在这方面为您提供很多帮助,只需使用此代码,您就可以检索一个仅包含目录的列表,并添加有用的信息,如(chmod,上次修改时间,大小…)。

代码:

// Connection
$connection = new FtpConnection("localhost", "foo", "12345");
$connection->open();
        
// FtpConfig
$config = new FtpConfig($connection);
$config->setPassive(true);
$client = new FtpClient($connection);
$allFolders =
    // directory, recursive, filter
    $client->listDirectoryDetails('/', true, FtpClient::DIR_TYPE); 
// Do whatever you want with the folders

此代码是Martin Prikryl代码的变体。它速度较慢,但没有任何空白区故障。只有当您对上面的代码有任何问题时,才使用此代码。

function ftp_list_files_recursive($ftp_stream, $path){
    $lines = ftp_nlist($ftp_stream, $path);
    $result = array();
    foreach ($lines as $line) {
        if (ftp_size($ftp_stream, $line) == -1) {
            $result = array_merge($result, ftp_list_files_recursive($ftp_stream, $line));
        }
        else{
            $result[] = $line;
        }
    }
    return $result;
}