PHP使用yield 讀取超大型目錄的方法

之前碰到一個問題,需要處理一個超大型目錄,目錄有多大呢,有200G大小,大部分人的思路如下, 用日常的遞歸,基本上讀取到的路徑數組非常大,會導致超出內存,特此研究了一番:

一般常見的方法如下:

function recursiveScan($dir) {
    $files = [];
    $items = scandir($dir);

    foreach ($items as $item) {
        if ($item == '.' || $item == '..') {
            continue;
        }

        $path = $dir . '/' . $item;

        if (is_dir($path)) {
            $files = array_merge($files, recursiveScan($path)); // 遞歸讀取子目錄
        } else {
            $files[] = $path;
        }
    }

    return $files;
}

$directory = '/path/to/directory';
$files = recursiveScan($directory);

foreach ($files as $file) {
    echo $file . PHP_EOL;
}

上面的方法是很常見的做法,讀取小文件夾沒問題,但是大文件夾會超出內存

解決思路:
1、在這個優化後的函數中,我們將 traverseFolder 函數改爲生成器函數,並使用 yield $path; 逐步生成文件路徑。這樣在調用這個函數時,可以逐步處理文件而不是一次性返回所有文件路徑。這種方式可以減少內存佔用,並在需要時逐個獲取文件路徑。
2、不使用遞歸這種耗資源的方式, 使用array_pop 棧的思路來模擬迭代

改進過的方法如下,非常的節省內存,默認128m的配置即可讀取超大目錄:

function traverseFolder($folder) {
    $stack = [$folder]; // 使用棧來模擬迭代

    $ext_map = $this->config('ext_map');
    $ext_map_arr = explode("\n", $ext_map);

    while (!empty($stack)) {
        $currentFolder = array_pop($stack);
        $handle = opendir($currentFolder);

        while (($file = readdir($handle)) !== false) {
            if ($file != '.' && $file != '..') {
                $path = $currentFolder . '/' . $file;

                if (is_dir($path)) {
                    $stack[] = $path;
                } else {
             
                    yield $path; // 生成器函數逐步生成文件路徑
                }
            }
        }

        closedir($handle);
    }
}

這樣就可以輕鬆讀取200G甚至2T的文件夾了,是不是很簡單了?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章