简体   繁体   中英

PHP FTP recursive directory listing

I'm trying to make a recursive function to get all the directories and sub directories from my ftp server in an array.

I tried a lot of functions I've found on the web. The one that works best for me is this one:

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 ;

}

The problem with this function is it wont allow the directory to have a "." in it's name and every file that is located in the root directory will be considered a directory as well. So I adjusted the function and got this:

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 ;

}

This works pretty good but and gives the result I want. but it's so slow it's unusable.

I also tried working with ftp_rawlist but it has the same drawback of being horribly slow.

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;
}

I've been stuck on this problem for the last couple of days and I'am getting desperate. If any one has any suggestion please let me know

If your server supports MLSD command and you have PHP 7.2 or newer, you can use 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;
} 

If you do not have PHP 7.2, you can try to implement the MLSD command on your own. For a start, see user comment of the ftp_rawlist command:
https://www.php.net/manual/en/function.ftp-rawlist.php#101071


If you cannot use MLSD , you will particularly have problems telling if an entry is a file or folder . While you can use the ftp_size trick, calling ftp_size for each entry can take ages.

But if you need to work against one specific FTP server only, you can use ftp_rawlist to retrieve a file listing in a platform-specific format and parse that.

The following code assumes a common *nix format.

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;
}

For DOS format, see: Get directory structure from FTP using PHP .

I've build an OOP FTP Client library that's can help you on this a lot, using just this code you can retrieve a list of only the directories with addition useful information like (chmod, last modified time, size ...).

The code :

// 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

This code a variation of Martin Prikryl one. It is slower but do not have any failures with whitespaces. Use this code only if you have any problems with the code above.

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;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM