獲取目錄及其所有子目錄下文件的總大小,是磁盤配額統計、備份評估、存儲監控的常見需求。利用 SPL 的 RecursiveDirectoryIterator 與 RecursiveIteratorIterator,只需一個 foreach 即可完成遞歸求和,無需手寫遞歸函數。
<?php
$dirPath = 'C:\\wamp'; // 或 /var/www/html
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dirPath, FilesystemIterator::SKIP_DOTS)
);
$totalSize = 0;
foreach ($iterator as $file) {
if ($file->isFile()) { // 僅統計文件,跳過目錄、鏈接等
$totalSize += $file->getSize();
}
}
// 友好輸出
echo "總大小:$totalSize 字節\n";
echo "約 " . round($totalSize / 1024 / 1024, 2) . " MB\n";
?>
FilesystemIterator::SKIP_DOTS:自動忽略 . 和 ..,避免無限循環。RecursiveIteratorIterator 預設為 LEAVES_ONLY(僅葉子節點),但為了語義清晰,示例中顯式判斷 $file->isFile()。SplFileInfo::getSize() 返回文件大小(字節),直接累加即可。<?php
/**
* 計算目錄總大小
*
* @param string $dir 目錄路徑
* @param bool $humanReadable 是否返回易讀字符串(如 1.23 MB)
* @return int|string 字節數或格式化字符串
*/
function getDirectorySize(string $dir, bool $humanReadable = false)
{
if (!is_dir($dir)) {
return $humanReadable ? '0 B' : 0;
}
$rii = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS)
);
$bytes = 0;
foreach ($rii as $file) {
if ($file->isFile()) {
$bytes += $file->getSize();
}
}
if (!$humanReadable) {
return $bytes;
}
$units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
$i = 0;
while ($bytes >= 1024 && $i < count($units) - 1) {
$bytes /= 1024;
$i++;
}
return round($bytes, 2) . ' ' . $units[$i];
}
// 使用示例
echo "原始字節:" . getDirectorySize('/var/log') . "\n";
echo "易讀格式:" . getDirectorySize('/var/log', true) . "\n"; // 輸出:12.45 MB
?>
UnexpectedValueException,生產環境建議用 try-catch 包裹或設置 FilesystemIterator::FOLLOW_SYMLINKS 視情況決定是否跟隨鏈接。SKIP_DOTS 不包含此行為),若需統計鏈接目標大小,可添加 FilesystemIterator::FOLLOW_SYMLINKS 旗標。C:\\path 或正斜杠 C:/path 均可。| 維度 | shell_exec('du -sh ...') | PHP RecursiveIterator |
|---|---|---|
| 速度 | 極快(C 級實現) | 較快(PHP 級遍歷) |
| 跨平台 | 依賴 Linux/Unix 工具 | 純 PHP,全平台通用 |
| 可控性 | 難以過濾特定文件/擴展名 | 可在循環中任意邏輯過濾 |
| 安全性 | 命令注入風險需 escapeshellarg | 無注入風險 |
利用 RecursiveDirectoryIterator + RecursiveIteratorIterator 計算目錄大小,代碼極其精簡、跨平台、可擴展。只需幾行代碼即可替代繁瑣的手寫遞歸,是 PHP 文件系統操作的最佳實踐之一。