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

PHP 統計目錄文件數量:高效、安全、避坑的完整指南

     編輯:PHP 相關知識

基礎版:遞歸遍歷計數

<?php
/**
 * 遞歸統計目錄下文件總數(含子目錄)
 * @param string $dir 目錄路徑
 * @return int 文件數量
 * @throws InvalidArgumentException 目錄不存在或不可讀
 */
function countFilesRecursive(string $dir): int
{
    if (!is_dir($dir) || !is_readable($dir)) {
        throw new InvalidArgumentException("目錄不存在或不可讀: $dir");
    }

    $count = 0;
    $iterator = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS),
        RecursiveIteratorIterator::LEAVES_ONLY
    );

    foreach ($iterator as $file) {
        if ($file->isFile()) {
            $count++;
        }
    }
    return $count;
}

// 使用範例
try {
    $total = countFilesRecursive(__DIR__ . '/pic');
    echo "圖片總數: $total"; // 輸出: 圖片總數: 100
} catch (Throwable $e) {
    echo "錯誤: " . $e->getMessage();
}
?>

進階版:支援過濾、限制深度、大目錄優化

<?php
/**
 * 進階文件計數器
 * @param string $dir 目錄路徑
 * @param array $options 選項:
 *   - 'extensions' => ['jpg','png'] 只統計指定副檔名(不含點)
 *   - 'maxDepth' => 3 最大遞歸深度(0=僅當前目錄)
 *   - 'followLinks' => false 是否跟隨符號鏈接
 *   - 'batchSize' => 1000 批次處理大小(大目錄避免記憶體峰值)
 * @return array ['total' => 總數, 'byExt' => 副檔名分佈, 'dirs' => 目錄數]
 */
function countFilesAdvanced(string $dir, array $options = []): array
{
    $extensions = array_map('strtolower', $options['extensions'] ?? []);
    $maxDepth = $options['maxDepth'] ?? -1; // -1 無限制
    $followLinks = $options['followLinks'] ?? false;
    $batchSize = $options['batchSize'] ?? 1000;

    $flags = FilesystemIterator::SKIP_DOTS | FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::CURRENT_AS_FILEINFO;
    if ($followLinks) $flags |= FilesystemIterator::FOLLOW_SYMLINKS;

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

    $total = 0;
    $dirs = 0;
    $byExt = [];
    $depth = 0;

    foreach ($iterator as $path => $file) {
        // 深度控制
        $currentDepth = $iterator->getDepth();
        if ($maxDepth >= 0 && $currentDepth > $maxDepth) {
            $iterator->next(); // 跳過子樹
            continue;
        }

        if ($file->isDir()) {
            $dirs++;
            continue;
        }

        if (!$file->isFile()) continue; // 跳過 socket、fifo 等

        // 副檔名過濾
        if ($extensions) {
            $ext = strtolower($file->getExtension());
            if (!in_array($ext, $extensions, true)) continue;
            $byExt[$ext] = ($byExt[$ext] ?? 0) + 1;
        }

        $total++;

        // 批次釋放記憶體(大目錄時)
        if ($total % $batchSize === 0) {
            gc_collect_cycles();
        }
    }

    return ['total' => $total, 'byExt' => $byExt, 'dirs' => $dirs];
}

// 使用範例:只統計圖片,最深 2 層
$result = countFilesAdvanced('/var/www/uploads', [
    'extensions' => ['jpg', 'jpeg', 'png', 'gif', 'webp'],
    'maxDepth' => 2,
]);
echo "圖片總數: {$result['total']}\n";
echo "分佈: " . json_encode($result['byExt'], JSON_UNESCAPED_UNICODE) . "\n";
echo "子目錄數: {$result['dirs']}\n";
?>

效能對比與選型建議

方法適用場景優點缺點
glob()單層、簡單模式代碼極簡不遞歸、大目錄記憶體峰值高、無法獲取文件元數據
scandir() + 遞歸函數教學、極簡需求無依賴易漏掉 ./..、手動遞歸易出錯、效能差
SPL RecursiveDirectoryIterator (推薦)生產環境、大目錄、需元數據內建 C 層實現、記憶體友好、支援深度控制、過濾、鏈接處理、並行遍歷API 稍繁瑣
exec('find ...')超大目錄(百萬級)、極致效能系統級優化、可用 wc -l 极快依賴 Shell、跨平台差、安全風險(注入)、需解析輸出

常見坑位與避坑指南

  1. 權限問題:is_readable() 檢查目錄權限,try-catch 捕獲 PermissionDenied(PHP 8.1+ 為 FilesystemIterator 拋出異常)。
  2. 符號鏈接循環:預設 SKIP_DOTS 不跟隨鏈接,若需跟隨必須加循環檢測(記錄已訪問 inode)。
  3. 隱藏文件:SKIP_DOTS 只跳過 ./..,**不跳過** .gitignore、.env 等點文件。如需排除,手動判斷 $file->getBasename()[0] === '.'。
  4. 編碼陷阱**:Windows 中文目錄名可能導致路徑編碼不一致,建議統一用 mb_convert_encoding 或 PHP 8.2+ 的 FilesystemIterator::CURRENT_AS_SELF 獲取 SplFileInfo 後自行處理。
  5. 效能陷阱**:網絡掛載目錄(NFS/SMB)遍歷極慢,避免在請求週期內同步執行,改用隊列異步統計 + 緩存結果。

一行命令快速統計(CLI 環境)

# 僅當前目錄文件數
php -r "echo count(array_diff(scandir('.'), ['.','..']));"

# 遞歸統計(含子目錄)
php -r "echo iterator_count(new RecursiveIteratorIterator(new RecursiveDirectoryIterator('.', FilesystemIterator::SKIP_DOTS)));"

# 統計特定副檔名
php -r "$c=0; foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator('.')) as $f) if($f->isFile() && in_array($f->getExtension(),['jpg','png'])) $c++; echo $c;"

總結

統計目錄文件數看似簡單,生產環境卻暗藏權限、編碼、效能、鏈接循環等坑。**推薦以 SPL RecursiveDirectoryIterator 為核心**,封裝為可複用函數/類,支援過濾、深度控制、批次釋放記憶體。超大規模場景下,異步化 + 緩存是正解。代碼即文檔,封裝好一次,到處安心用。

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