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

PHP 遞迴計算目錄總大小實作與效能優化

     編輯:PHP 綜合知識

基本遞迴實作

<?php
/**
 * 遞迴計算目錄總大小(位元組)
 * @param string $directory 目錄絕對路徑
 * @return int|false 總位元組數,失敗回傳 false
 */
function getDirectorySize($directory) {
    $directory = rtrim($directory, '/\\') . DIRECTORY_SEPARATOR;
    
    if (!is_dir($directory) || !is_readable($directory)) {
        return false;
    }
    
    $size = 0;
    $handle = opendir($directory);
    if (!$handle) return false;
    
    while (($entry = readdir($handle)) !== false) {
        if ($entry === '.' || $entry === '..') continue;
        
        $path = $directory . $entry;
        
        if (is_dir($path)) {
            $subSize = getDirectorySize($path);
            if ($subSize === false) return false;
            $size += $subSize;
        } elseif (is_file($path)) {
            $size += filesize($path);
        }
        // 忽略連結、裝置等特殊檔案
    }
    
    closedir($handle);
    return $size;
}

// 使用
$bytes = getDirectorySize('/var/www/html');
if ($bytes !== false) {
    echo formatBytes($bytes); // 見下方格式化函數
}

/** 位元組轉人類可讀格式 */
function formatBytes($bytes, $precision = 2) {
    $units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
    $bytes = max($bytes, 0);
    $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
    $pow = min($pow, count($units) - 1);
    $value = $bytes / (1024 ** $pow);
    return round($value, $precision) . ' ' . $units[$pow];
}
?>

SPL 迭代器版本(更現代、可中斷)

<?php
function getDirectorySizeSPL($directory) {
    $iterator = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($directory, RecursiveDirectoryIterator::SKIP_DOTS),
        RecursiveIteratorIterator::LEAVES_ONLY // 只遍歷檔案,不進入目錄節點
    );
    
    $size = 0;
    foreach ($iterator as $file) {
        if ($file->isFile()) {
            $size += $file->getSize();
        }
    }
    return $size;
}

// 進階:支援排除模式、最大深度、進度回調
function getDirectorySizeAdvanced($directory, array $options = []) {
    $defaults = [
        'exclude_patterns' => ['/\.git/', '/node_modules/', '/vendor/', '/\.cache/'],
        'max_depth' => -1,
        'follow_links' => false,
        'progress_callback' => null, // function(int $currentBytes, int $fileCount)
    ];
    $opts = array_merge($defaults, $options);
    
    $iterator = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator(
            $directory,
            ($opts['follow_links'] ? 0 : RecursiveDirectoryIterator::FOLLOW_SYMLINKS) |
            RecursiveDirectoryIterator::SKIP_DOTS |
            RecursiveDirectoryIterator::KEY_AS_PATHNAME
        ),
        RecursiveIteratorIterator::LEAVES_ONLY
    );
    
    if ($opts['max_depth'] > 0) {
        $iterator = new RegexIterator($iterator, '/^.{' . $opts['max_depth'] . '}/'); // 簡易深度限制
    }
    
    $size = 0;
    $count = 0;
    
    foreach ($iterator as $path => $file) {
        // 排除模式檢查
        $excluded = false;
        foreach ($opts['exclude_patterns'] as $pattern) {
            if (preg_match($pattern, $path)) {
                $excluded = true;
                break;
            }
        }
        if ($excluded) continue;
        
        if ($file->isFile()) {
            $size += $file->getSize();
            $count++;
            
            if ($opts['progress_callback'] && $count % 1000 === 0) {
                $opts['progress_callback']($size, $count);
            }
        }
    }
    
    return $size;
}
?>

系統命令版(大目錄最高效能)

<?php
function getDirectorySizeCLI($directory) {
    // Linux/Unix: du -sb (bytes, 不跟隨符號連結)
    // macOS: du -sk (KB) 再 * 1024
    $directory = escapeshellarg($directory);
    
    if (PHP_OS_FAMILY === 'Windows') {
        // Windows: 使用 dir /s 或 PowerShell
        $cmd = "powershell -Command \"(Get-ChildItem -Recurse -Force $directory | Measure-Object -Property Length -Sum).Sum\"";
    } else {
        $cmd = "du -sb $directory 2>/dev/null";
    }
    
    $output = shell_exec($cmd);
    $size = (int)trim($output);
    
    return $size > 0 ? $size : false;
}

/** 自動選擇最佳方法 */
function getDirectorySizeAuto($directory) {
    // 嘗試系統命令(最快)
    $size = getDirectorySizeCLI($directory);
    if ($size !== false) return $size;
    
    // 回退 SPL 迭代器
    return getDirectorySizeSPL($directory);
}
?>

效能比較與選擇建議

方法適用規模優點缺點
遞迴函數< 1 萬檔案純 PHP、可攜、易理解函數呼叫開銷大、易超出記憶體/執行時間
SPL 迭代器< 10 萬檔案記憶體友善、可中斷、支援過濾PHP 迴圈仍較慢
系統命令 du任意規模C 語言實作、核心優化、極快平台相依、需 shell 執行權限、Windows 不同
資料庫/快取記錄頻繁查詢毫秒級回應需維護同步機制

生產環境最佳實踐

  1. 非同步計算:佇列任務(如 Laravel Queue、Gearman),背景計算後存入 Redis
  2. 增量更新:檔案異動時(inotify、Flysystem 事件)只更新差值
  3. 分層快取:目錄大小快取 1 小時,檔案數快取 10 分鐘
  4. 監控告警:目錄超過閾值(如 100GB)自動通知
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved