Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
577 views
in Technique[技术] by (71.8m points)

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

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...