首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 遞歸計算目錄大小:含單位轉換、權限處理、效能優化的完整實現

     編輯:PHP 相關知識

核心遞歸函式

<?php
/**
 * 計算目錄總大小(位元組)
 *
 * @param string $dir 絕對路徑
 * @return int|false 位元組數,失敗返回 false
 */
function dirSize(string $dir): int|false {
    $dir = rtrim($dir, '/\\');
    if (!is_dir($dir) || !is_readable($dir)) {
        return false;
    }

    $size = 0;
    $handle = @opendir($dir);
    if (!$handle) return false;

    while (($entry = readdir($handle)) !== false) {
        if ($entry === '.' || $entry === '..') continue;

        $path = $dir . DIRECTORY_SEPARATOR . $entry;

        if (is_link($path)) {
            // 符號連結:可選擇跳過、計算連結本身大小、或遞歸目標
            // 這裡跳過,避免循環引用
            continue;
        } elseif (is_dir($path)) {
            $subSize = dirSize($path);
            if ($subSize === false) continue; // 無權限讀取子目錄,跳過
            $size += $subSize;
        } elseif (is_file($path)) {
            $size += filesize($path);
        }
    }
    closedir($handle);
    return $size;
}

人類可讀單位轉換

<?php
function formatBytes(int $bytes, int $precision = 2): string {
    $units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
    $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
    $pow = min($pow, count($units) - 1);
    $value = $bytes / (1024 ** $pow);
    return round($value, $precision) . ' ' . $units[$pow];
}

// 使用
$path = '/var/www/html';
$bytes = dirSize($path);
if ($bytes !== false) {
    echo "目錄大小: " . formatBytes($bytes); // 例:123.45 MB
} else {
    echo "無法讀取目錄: $path";
}

進階優化:迭代器模式避免遞歸深度限制

<?php
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;

function dirSizeIterator(string $dir): int|false {
    if (!is_dir($dir) || !is_readable($dir)) return false;

    $iterator = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
        RecursiveIteratorIterator::SELF_FIRST
    );

    $size = 0;
    foreach ($iterator as $file) {
        if ($file->isFile() && !$file->isLink()) {
            $size += $file->getSize();
        }
    }
    return $size;
}
  • 無函數調用堆疊溢位風險(PHP 預設無限制,但極深目錄樹可能耗盡記憶體)
  • 內建跳過 ./..、支援 SKIP_DOTS、可過濾 isLink()
  • 效能略優於手寫遞歸(C 層實現)

權限與錯誤處理最佳實踐

場景處理策略
目錄不可讀返回 false,上層決定是否拋出異常或記錄日誌
子目錄不可讀跳過該子樹,累加可讀部分,可選記錄警告
符號連結循環RecursiveDirectoryIterator::FOLLOW_SYMLINKS **不可設**,或手動維護 SplObjectStorage 追蹤已訪問 inode
超大目錄(百萬檔)分批處理、生成器 yield 避免記憶體峰值、或改用 du -sb 系統命令

系統命令替代方案(極大目錄最快)

<?php
function dirSizeDu(string $dir): int|false {
    $dir = escapeshellarg($dir);
    $output = [];
    $returnVar = 0;
    // -s: summarize, -b: bytes, --exclude 排除模式
    exec("du -sb $dir 2>/dev/null", $output, $returnVar);
    if ($returnVar !== 0 || empty($output)) return false;
    // 輸出格式: "12345678\t/path/to/dir"
    return (int)explode("\t", $output[0])[0];
}

優點: 核心級實現、極快、自動處理硬連結/稀疏檔/掛載點。
缺點: 依賴 du 命令(Linux/Unix)、需 exec 權限、Windows 不支援。

快取策略:避免重複計算

<?php
class DirectorySizeCache {
    private array $cache = [];
    private int $ttl = 300; // 5 分鐘

    public function getSize(string $dir): int|false {
        $key = realpath($dir) ?: $dir;
        if (isset($this->cache[$key]) && (time() - $this->cache[$key]['time'] < $this->ttl)) {
            return $this->cache[$key]['size'];
        }
        $size = dirSizeIterator($key);
        if ($size !== false) {
            $this->cache[$key] = ['size' => $size, 'time' => time()];
        }
        return $size;
    }
}

總結

PHP 計算目錄大小的標準解是 RecursiveDirectoryIterator + RecursiveIteratorIterator:簡潔、健壯、效能佳。搭配單位格式化、權限容錯、快取機制,即可滿足從後台管理面板到自動化部署腳本的各種需求。極大目錄(>100 萬檔)建議改用 du -sb 系統命令或離線預計算任務。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved