之前碰到一個問題,需要處理一個超大型目錄,目錄有多大呢,有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的文件夾了,是不是很簡單了?