昨天有个同事找我调一个老项目,历目录文说上传的历目录文图片在服务器上显示不全。我打开FTP一看,历目录文目录结构倒是历目录文整齐,可代码里用的历目录文是最原始的opendir()配合readdir()硬扫。三层嵌套循环,历目录文每层还带着if ($file != '.' && $file != '..')这种判断,历目录文看着就头疼。历目录文
PHP遍历目录这事儿,历目录文真没多玄乎。历目录文核心就三个函数:scandir()、历目录文glob()、历目录文以及RecursiveDirectoryIterator。历目录文但选哪个,历目录文得看你想要什么。历目录文
先说scandir()。它把整个目录一次性读进数组,简单粗暴。适合小目录,比如几十个文件那种。代码长这样:
$files = scandir('/path/to/dir'); foreach ($files as $file) { if (is_file('/path/to/dir/' . $file)) { echo $file; } } 注意它会把.和..也带进来,必须过滤。还有路径拼接时要小心,少了斜杠就是另一个坑。
遇到大目录,比如几万张图片,scandir()就有点扛不住——一次性全加载到内存,服务器直接喘粗气。这时候用glob()更聪明,支持通配符,比如只抓jpg:
$images = glob('/path/to/dir/*.jpg'); 它返回的是完整路径,省得你自己拼。但glob()也有脾气——默认不抓隐藏文件,而且对子目录无能为力。
真正让我觉得“这才叫遍历”的,是RecursiveDirectoryIterator。这家伙配合RecursiveIteratorIterator,能像推土机一样碾过整个目录树,不管嵌套多深。去年我处理一个客户上传的素材库,三层子目录,两千多个文件,用这个迭代器十几行搞定:
$iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator('/path/to/dir') ); foreach ($iterator as $file) { if ($file->isFile()) { echo $file->getPathname() . PHP_EOL; } } 注意RecursiveDirectoryIterator默认也会把.和..当目录项吐出来,得用FilesystemIterator::SKIP_DOTS标志位干掉:
new RecursiveDirectoryIterator('/path', FilesystemIterator::SKIP_DOTS) 还有个冷门但实用的场景——按修改时间排序。直接遍历出来的顺序是乱的,要是想找最近改过的文件,得先收集再排序。用RecursiveDirectoryIterator配合filemtime(),攒个数组用usort()排一下,比手写递归干净多了。
说个真实翻车案例。之前有个同事图省事,直接glob('uploads/*'),结果项目后来加了子目录存缩略图,前端死活找不到图。查了半天才发现glob()不递归子目录。后来换迭代器,问题秒解。
最后提性能。遍历几千个文件,哪个函数都差不了一毫秒。真正卡的是后面——比如你得对每个文件做getimagesize()或者读EXIF。这时候别急着在循环里干重活,先收集路径,再分批处理,或者用yield搞个生成器,内存占用能降一个量级。
写代码这事,工具选对了,后面全是顺水推舟。别上来就套模板,先看看你的目录长什么样——平铺的还是树状的?文件多不多?需不需要过滤?搞清楚了,三行代码和三十行代码,差别就在这儿。