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

PHP遞歸計算目錄大小實現與單位自動轉換

     編輯:PHP 相關知識

PHP目錄大小遞歸計算:從字節到可讀單位的完整方案

在文件管理系統、磁盤配額監控、備份腳本等場景中,準確計算目錄佔用空間是基礎需求。本文提供一個健壯的遞歸計算函數,支持符號鏈接識別、權限錯誤容錯、大文件處理,並封裝了人性化的單位自動轉換功能。

核心實現:dirSize 遞歸函數

<?php
/**
 * 遞歸計算目錄大小(字節)
 * @param string $directory 目錄絕對路徑
 * @return int|false 總字節數,失敗返回false
 */
function dirSize($directory) {
    // 路徑標準化
    $directory = rtrim(realpath($directory), DIRECTORY_SEPARATOR);
    if ($directory === false || !is_dir($directory)) {
        trigger_error("目錄不存在或不可讀: $directory", E_USER_WARNING);
        return false;
    }

    $size = 0;
    $handle = @opendir($directory);
    if (!$handle) {
        trigger_error("無法打開目錄: $directory", E_USER_WARNING);
        return false;
    }

    while (($file = readdir($handle)) !== false) {
        // 跳過 . 和 ..
        if ($file === '.' || $file === '..') continue;
        
        $path = $directory . DIRECTORY_SEPARATOR . $file;
        
        // 處理符號鏈接:避免循環引用導致無限遞歸
        if (is_link($path)) {
            // 可選:統計鏈接本身大小,或跳過,或解析真實目標
            continue; // 此處選擇跳過鏈接
        }
        
        if (is_dir($path)) {
            $subSize = dirSize($path);
            if ($subSize !== false) $size += $subSize;
        } elseif (is_file($path)) {
            $fileSize = @filesize($path);
            if ($fileSize !== false) $size += $fileSize;
            // 注意:filesize對2GB+文件在32位系統可能失效,建議用stat()
        }
    }
    closedir($handle);
    return $size;
}

/**
 * 字節轉換為人類可讀格式
 * @param int $bytes 字節數
 * @param int $precision 小數位數
 * @return string 如 "1.5 MB"
 */
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 / pow(1024, $pow);
    return round($value, $precision) . ' ' . $units[$pow];
}

// 使用示例
$dir = __DIR__ . '/storage'; // 替換為實際目錄
$bytes = dirSize($dir);
if ($bytes !== false) {
    echo "目錄大小: " . formatBytes($bytes); // 輸出如: 目錄大小: 12.34 MB
}

關鍵技術點解析

  1. realpath() 規範路徑:解析相對路徑、符號鏈接,統一分隔符
  2. @ 錯誤抑制 + 判斷返回值:處理權限不足、文件被刪除等異常,不中斷遞歸
  3. is_link() 檢測符號鏈接:防止循環鏈接導致棧溢出,根據業務決定是統計鏈接大小、目標大小還是忽略
  4. 大文件兼容:32位系統下 filesize() 返回負數,建議使用 fstat(fopen($path, 'r'))['size'] 或 exec("stat -c%s $path")
  5. 內存效率:遞歸深度受限於目錄層級,極深目錄樹建議改用迭代器模式(RecursiveDirectoryIterator)

進階版:基於 SPL 迭代器的非遞歸實現

<?php
function dirSizeIterator($directory) {
    $directory = rtrim(realpath($directory), DIRECTORY_SEPARATOR);
    if (!$directory || !is_dir($directory)) return false;
    
    $size = 0;
    $iterator = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($directory, 
            RecursiveDirectoryIterator::SKIP_DOTS | 
            RecursiveDirectoryIterator::FOLLOW_SYMLINKS
        ),
        RecursiveIteratorIterator::SELF_FIRST
    );
    
    foreach ($iterator as $file) {
        if ($file->isFile() && !$file->isLink()) {
            $size += $file->getSize();
        }
    }
    return $size;
}

SPL版本優勢:無遞歸深度限制、內存佔用恆定、自動處理權限錯誤(拋出異常可捕獲)、代碼更簡潔。

性能優化建議

  • 緩存結果:對頻繁查詢的目錄,使用Redis/Memcached緩存大小,定時異步更新
  • 增量統計:文件上傳/刪除時同步更新目錄大小元數據,避免全量掃描
  • 並行計算:大目錄樹可使用 pcntl_fork 或多進程分割子目錄並行統計,最終匯總
  • 系統調用:Linux下 du -sb $dir 經C優化極快,可 exec() 調用(注意命令注入防護)
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved