如何使用 PHP 计算具有 FTP 访问权限的整个目录大小

How to calculate entire directory size with FTP access using PHP

我为客户设置了许多不同的托管帐户,需要计算每个帐户使用的存储量 space,该存储量会定期更新。

我设置了一个数据库来记录每个客户端的存储使用情况。

我首先尝试在每个帐户上使用 PHP 文件,运行 通过 Cron 作业。如果我自己手动 运行,它会输出正确的文件大小并将正确的大小更新到数据库,尽管当从 Cron 作业 运行 时,它会输出 0。

然后我尝试从主帐户的 Cron 作业中 运行 这个文件,但认为这实际上行不通,因为我的主机会阻止来自另一台服务器的文件,我最终会得到相同的结果和以前一样。

我现在正在尝试 FTP 从主帐户的 Cron 作业访问每个帐户,如下所示,唯一的问题是我不知道如何计算目录大小而不是单个文件大小使用 FTP 访问,不知道如何重申这种方式?希望在我最终绕圈子之前有人可以在这里提供帮助吗?

我也会加上之前的第一次尝试

$ftp_conn = ftp_connect($ftp_host, 21, 420) or die("Could not connect to server");
$ftp_login = ftp_login($ftp_conn, $ftp_username, 'mypassword');

$total_size = 0;                    
$contents = ftp_nlist($ftp_conn, ".");
// output $contents
foreach($contents as $folder){

    while($search == true){

        if($folder == '..' || $folder == '.'){

        } else {
            $file = $folder; 
            $res = ftp_size($ftp_conn, $file);

            if ($res != -1) {
                $total_size = $total_size + $res;
            } else {
                $total_size = $total_size;
            }
        }
    }
}

ftp_close($ftp_conn); 

这不起作用,因为它不计算文件夹大小,而且我不知道如何使用此方法打开重复?


第二个脚本确实有效,但只有在手动打开时才会有效,如果 运行 由 cron 作业打开,则 return 0。

class Directory_Calculator {

    function calculate_whole_directory($directory)
    {
        if ($handle = opendir($directory))
        {
            $size = 0;
            $folders = 0;
            $files = 0;

            while (false !== ($file = readdir($handle)))
            {
                if ($file != "." && $file != "..")
                {
                    if(is_dir($directory.$file))
                    {
                    $array = $this->calculate_whole_directory($directory.$file.'/');
                    $size += $array['size'];
                    $files += $array['files'];
                    $folders += $array['folders'];
                    }
                    else
                    {
                    $size += filesize($directory.$file);
                    $files++;
                    }
                }
            }
            closedir($handle);
        }

        $folders++;

        return array('size' => $size, 'files' => $files, 'folders' => $folders);
    }

}

/* Path to Directory - IMPORTANT: with '/' at the end */
$directory = '../public_html/'; 

// return an array with: size, total files & folders
$array = $directory_size->size($directory); 

$size_of_site = $array['size'];

echo $size_of_site;

请记住,我目前正在测试 none 的 MySQLi 或 PHP 脚本是安全的。

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

function calculate_whole_directory($ftp_conn, $directory)
{
    $files = ftp_mlsd($ftp_conn, $directory) or die("Cannot list $directory");
    $result = 0;
    foreach ($files as $file)
    { 
        if (($file["type"] == "cdir") || ($file["type"] == "pdir"))
        {
            $size = 0;
        }
        else if ($file["type"] == "dir")
        {
            $size = calculate_whole_directory($ftp_conn, $directory."/".$file["name"]);
        }
        else
        {
            $size = intval($file["size"]);
        }
        $result += $size;
    } 

    return $result; 
} 

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


如果您不能使用 MLSD,您将特别难以判断每个条目的 . While you can use the ftp_size trick, as you do, calling ftp_size 是否需要很长时间。

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

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

function calculate_whole_directory($ftp_conn, $directory)
{
    $lines = ftp_rawlist($ftp_conn, $directory) or die("Cannot list $directory");
    $result = 0;
    foreach ($lines as $line)
    { 
        $tokens = preg_split("/\s+/", $line, 9); 
        $name = $tokens[8];
        if ($tokens[0][0] === 'd')
        {
            $size = calculate_whole_directory($ftp_conn, "$directory/$name");
        }
        else
        {
            $size = intval($tokens[4]);
        }
        $result += $size;
    } 

    return $result; 
} 

基于.


关于 cron:我猜 cron 没有以正确的工作目录启动脚本,因此您计算了一个不存在的目录的大小。

这里使用绝对路径:

$directory = '../public_html/'; 

尽管您最好添加一些错误检查,以便您自己可以看到哪里出了问题。