PHP FTP递归目录列表

Posted

技术标签:

【中文标题】PHP FTP递归目录列表【英文标题】:PHP FTP recursive directory listing 【发布时间】:2016-03-30 13:26:07 【问题描述】:

我正在尝试创建一个递归函数以从我的 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;

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

【问题讨论】:

【参考方案1】:

如果您的服务器支持MLSD 命令并且您有php 7.2 或更高版本,您可以使用ftp_mlsd function:

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;
 

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


如果您不能使用MLSD,您将特别难以判断entry is a file or folder。虽然您可以使用 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 格式见:Get directory structure from FTP using PHP。

【讨论】:

【参考方案2】:

我已经构建了一个 OOP FTP Client library,它可以在这方面为您提供很多帮助,仅使用此代码,您就可以检索仅包含其他有用信息的目录列表,例如(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

【讨论】:

【参考方案3】:

此代码是 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;

【讨论】:

我已经更新了我的答案,这样它就可以处理空格了——但效率很高,无需为每个文件调用ftp_size

以上是关于PHP FTP递归目录列表的主要内容,如果未能解决你的问题,请参考以下文章

thinkphp5获取ftp上的文件列表

用于在没有 -R 的 FTP 服务器上列出递归目录的 Bash 脚本

ftp列表错误,flashfxp列表错误,ftp无法列目录的解决方法

ftp(filezilla)获取目录列表失败

FTP错误代码列表

FTP错误代码列表