php scandir 流实现
PHP scandir 流式实现
PHP 的 scandir 函数通常用于获取目录中的文件和子目录列表,但它是一次性加载所有内容到内存中。如果需要流式处理(逐项读取或处理大型目录),可以通过以下方法实现。
使用 DirectoryIterator 或 FilesystemIterator
PHP 提供了 DirectoryIterator 和 FilesystemIterator 类,支持流式遍历目录内容,避免一次性加载全部文件。
$iterator = new DirectoryIterator('/path/to/directory');
foreach ($iterator as $fileinfo) {
if ($fileinfo->isDot()) continue; // 跳过 "." 和 ".."
echo $fileinfo->getFilename() . "\n";
}
DirectoryIterator是轻量级的目录遍历工具。isDot()用于过滤当前目录(.)和父目录(..)。getFilename()返回文件名。
使用 RecursiveDirectoryIterator 递归遍历
如果需要递归遍历子目录,可以使用 RecursiveDirectoryIterator 结合 RecursiveIteratorIterator。
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator('/path/to/directory'),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $fileinfo) {
if ($fileinfo->isDot()) continue;
echo $fileinfo->getPathname() . "\n";
}
RecursiveIteratorIterator::SELF_FIRST表示先遍历当前目录,再进入子目录。getPathname()返回文件的完整路径。
使用 Generator 实现自定义流式处理
通过生成器(Generator)可以进一步控制流式处理的逻辑,例如按需加载或分块处理。
function streamDirectory($path) {
$iterator = new DirectoryIterator($path);
foreach ($iterator as $fileinfo) {
if ($fileinfo->isDot()) continue;
yield $fileinfo->getFilename();
}
}
foreach (streamDirectory('/path/to/directory') as $filename) {
echo $filename . "\n";
}
- 生成器通过
yield逐项返回结果,减少内存占用。 - 适用于需要延迟处理或分批次加载的场景。
性能与内存优化
- 避免一次性加载:
scandir返回数组会占用更多内存,而迭代器逐项处理更高效。 - 过滤早期:在遍历时尽早过滤无关文件(如隐藏文件),减少后续处理开销。
- 递归控制:对于大型目录树,可以通过
RecursiveIteratorIterator的深度控制(setMaxDepth)限制遍历层级。
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator('/path/to/directory')
);
$iterator->setMaxDepth(2); // 仅遍历 2 层深度
注意事项
- 符号链接:默认情况下,迭代器会跟随符号链接。若需禁用,可设置
FilesystemIterator::SKIP_DOTS。 - 异常处理:遍历时可能因权限问题抛出异常,建议用
try-catch包裹逻辑。 - 性能权衡:流式处理虽节省内存,但可能增加 I/O 操作次数,需根据场景选择。
通过以上方法,可以实现高效、低内存占用的目录流式遍历。







