如何使用PHP脚本循环遍历文件夹中的所有文件?

170
我正在寻找一个PHP脚本,它可以循环遍历目录中的所有文件,以便我可以对文件名进行格式化、打印或将其添加到链接中等操作。我希望能够按名称、类型或创建/添加/修改日期对文件进行排序(类似于 fancy 目录索引)。此外,我还想在文件列表中添加不需要的文件,例如脚本本身或其他“系统”文件(如“.”和“..”目录)。
由于我希望能够修改脚本,因此更有兴趣查看 PHP 文档并学习如何编写自己的脚本。如果有现有的脚本、教程或其他资源,请让我知道。

https://dev59.com/33NA5IYBdhLWcg3wHp-B#1086110 - zloctb
10个回答

295
您可以使用DirectoryIterator。以下是来自PHP手册的示例:
<?php
$dir = new DirectoryIterator(dirname(__FILE__));
foreach ($dir as $fileinfo) {
    if (!$fileinfo->isDot()) {
        var_dump($fileinfo->getFilename());
    }
}
?>

3
注意:许多服务器没有安装SPL,因此您将无法使用DirectoryIterator类(请参见下面的替代帖子)。尽管如此,请尽可能使用它! - NexusRex
7
注意[2]:确保您理解上面的“dirname()”函数将获取您放置在那里的任何路径的父文件夹。在我的情况下,我假设dirname是目录名称/路径的包装器,因此不需要它。 - willdanceforfun
另外,如果dirname是一个大型文件系统,内存问题就显而易见了。在我的案例中有100万个文件,应用程序需要在memory_limit上约使用512M的RAM。 - abkrim
2
如果你需要像/home/examples/banana.jpg这样的完整路径,请使用$fileinfo->getPathname() - mgutt
1
您可以使用 !$fileinfo->isDir() 来避免在目录上执行操作。 - LeChatNoir
这个例子是如何工作的?"DirectoryIterator(dirname(FILE))"会在服务器上搜索整个文件夹层次结构吗?如果是,那不是会导致性能非常慢吗? - Eight Lives

51

如果您无法访问DirectoryIterator类,请尝试这样做:

<?php
$path = "/path/to/files";

if ($handle = opendir($path)) {
    while (false !== ($file = readdir($handle))) {
        if ('.' === $file) continue;
        if ('..' === $file) continue;

        // do something with the file
    }
    closedir($handle);
}
?>

4
你能说出一个无法获取它的情况吗? - Jochem Kuijpers
12
许多传统应用程序使用PHP 4,该版本无法访问DirectoryIterator。 - Joseph Callaars
1
为什么 $file === '.'?这不是 Java。 - Dave Heq
2
Dave...不,它会匹配点并且如果在PHP中不匹配则不会继续。搜索“==”和“===”之间的区别。 - JSG

39

使用scandir()函数:

<?php
    $directory = '/path/to/files';

    if (!is_dir($directory)) {
        exit('Invalid diretory path');
    }

    $files = array();
    foreach (scandir($directory) as $file) {
        if ($file !== '.' && $file !== '..') {
            $files[] = $file;
        }
    }

    var_dump($files);
?>

23

您还可以使用FilesystemIterator。它所需的代码甚至比DirectoryIterator还要少,并自动删除 . ..

// Let's traverse the images directory
$fileSystemIterator = new FilesystemIterator('images');

$entries = array();
foreach ($fileSystemIterator as $fileInfo){
    $entries[] = $fileInfo->getFilename();
}

var_dump($entries);

//OUTPUT
object(FilesystemIterator)[1]

array (size=14)
  0 => string 'aa[1].jpg' (length=9)
  1 => string 'Chrysanthemum.jpg' (length=17)
  2 => string 'Desert.jpg' (length=10)
  3 => string 'giphy_billclinton_sad.gif' (length=25)
  4 => string 'giphy_shut_your.gif' (length=19)
  5 => string 'Hydrangeas.jpg' (length=14)
  6 => string 'Jellyfish.jpg' (length=13)
  7 => string 'Koala.jpg' (length=9)
  8 => string 'Lighthouse.jpg' (length=14)
  9 => string 'Penguins.jpg' (length=12)
  10 => string 'pnggrad16rgb.png' (length=16)
  11 => string 'pnggrad16rgba.png' (length=17)
  12 => string 'pnggradHDrgba.png' (length=17)
  13 => string 'Tulips.jpg' (length=10)

链接: http://php.net/manual/zh/class.filesystemiterator.php


6
您可以使用以下代码递归地循环遍历目录:

$path = "/home/myhome";
$rdi = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::KEY_AS_PATHNAME);
foreach (new RecursiveIteratorIterator($rdi, RecursiveIteratorIterator::SELF_FIRST) as $file => $info) {
    echo $file."\n";
}

4

glob() 函数支持排序和模式匹配。由于返回值是一个数组,因此您可以处理大多数其他需要的事情。


2
除非你处理的文件很少,否则这并不好...>10,000。你会用完内存的。 - NexusRex
@NexusRex:你也不应该从数据库中读取10,000条记录,但这超出了问题的范围。 - bcosca
同意!如果从数据库中读取,您可以使用“limit”进行分页,但是当您需要迭代遍历包含500万个XML文件的目录时,就没有这样的运气了。 - NexusRex
有 SPL GlobIterator。 - przemo_li
这对于正确的使用来说是太棒了。在我的情况下,我想每晚清空小公司网站的下载文件夹。我希望能够在下载文件夹中有子目录,而这正是我一直在寻找的解决方案。感谢您的发布! - Jon Vote

4

在大多数情况下,我想你可能想跳过 . .. 。这里是使用递归实现:

<?php

$rdi = new RecursiveDirectoryIterator('.', FilesystemIterator::SKIP_DOTS);
$rii = new RecursiveIteratorIterator($rdi);

foreach ($rii as $di) {
   echo $di->getFilename(), "\n";
}

https://php.net/class.recursivedirectoryiterator


2

为了完整性(因为这似乎是一个高流量的页面),让我们不要忘记那个老古董的dir()函数

$entries = [];
$d = dir("/"); // dir to scan
while (false !== ($entry = $d->read())) { // mind the strict bool check!
    if ($entry[0] == '.') continue; // ignore anything starting with a dot
    $entries[] = $entry;
}
$d->close();
sort($entries); // or whatever desired

print_r($entries);

0

你也可以这样做

$path = "/public";

$objects = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::SELF_FIRST);

foreach ($objects as $name => $object) {
  if ('.' === $object) continue;
  if ('..' === $object) continue;

str_replace('/public/', '/', $object->getPathname());

// for example : /public/admin/image.png => /admin/image.png

0
使用接受答案的建议DirectoryIterator存在问题。毕竟,只需加载迭代器的文档页面,看看是否有任何问题--实际上,查看可用的方法。你能看到directoryiterator有没有确定文件夹中的条目是文件夹还是其他的方法吗?为什么没有isFolder()isDirectory()或可以在文件上调用的任何方法呢?有getFilename(),但为什么我不能确定是否有另一个需要另一个directoryiterator的文件夹呢?
在这种情况下,您需要将is_dir()DirectoryIterator结合使用。以下是一个递归示例,将文件与文件夹分开,然后递归地迭代新发现的文件夹:
$dir = '/some_directory/';

function parseDir($dir) {
    foreach (new DirectoryIterator($dir) as $file) {
        if($file->isDot()) continue;
        $new_item = $dir . $file->getFilename() . '/';
        if(is_dir($new_item)) {
            print('Directory found!' . $new_item . PHP_EOL);
            parseDir($new_item);    # Recursion
        } else {
            print('File found!' . $new_item . PHP_EOL);
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接