PHP glob - 在子文件夹中扫描文件

54

我有一个服务器,里面有许多文件分布在不同的文件夹、子文件夹和子子文件夹中。

我想创建一个search.php页面,用于在整个服务器上搜索特定的文件。如果找到该文件,则返回位置路径以显示下载链接。

以下是目前的进展:

$root = $_SERVER['DOCUMENT_ROOT'];
$search = "test.zip";
$found_files = glob("$root/*/test.zip");
$downloadlink = str_replace("$root/", "", $found_files[0]);
if (!empty($downloadlink)) {
    echo "<a href=\"http://www.example.com/$downloadlink\">$search</a>";
} 

如果文件在我的域名根目录下,脚本将正常工作...现在我正在尝试找到一种方法,使其也可以扫描子文件夹和子子文件夹,但我卡在这里了。


http://stackoverflow.com/questions/8870731/scan-files-in-a-directory-and-sub-directory-and-store-their-path-in-array-using - open source guy
你可能会更幸运地使用 file_exists() 函数。http://php.net/manual/en/function.file-exists.php (或混合使用)。 - Funk Forty Niner
不告诉我如何扫描所有子文件夹和子子文件夹以查找文件... - Winston Smith
没错。你看了messi fan发的链接吗?看起来很有前途。我现在正在尝试它,它可以显示起始文件夹和子文件夹中的所有文件,但不是按照你想要的方式工作。另外,我的两只眼睛都在同一个眼窝里,需要休息了,很快就会睡觉。 - Funk Forty Niner
4个回答

92

有两种方法。

使用 glob 进行递归搜索:

<?php
 
// Does not support flag GLOB_BRACE
function rglob($pattern, $flags = 0) {
    $files = glob($pattern, $flags); 
    foreach (glob(dirname($pattern).'/*', GLOB_ONLYDIR|GLOB_NOSORT) as $dir) {
        $files = array_merge(
            [],
            ...[$files, rglob($dir . "/" . basename($pattern), $flags)]
        );
    }
    return $files;
}

// usage: to find the test.zip file recursively
$result = rglob($_SERVER['DOCUMENT_ROOT'] . '/test.zip');
var_dump($result);
// to find the all files that names ends with test.zip
$result = rglob($_SERVER['DOCUMENT_ROOT'] . '/*test.zip');
?>

使用RecursiveDirectoryIterator

<?php
// $regPattern should be using regular expression
function rsearch($folder, $regPattern) {
    $dir = new RecursiveDirectoryIterator($folder);
    $ite = new RecursiveIteratorIterator($dir);
    $files = new RegexIterator($ite, $regPattern, RegexIterator::GET_MATCH);
    $fileList = array();
    foreach($files as $file) {
        $fileList = array_merge($fileList, $file);
    }
    return $fileList;
}

// usage: to find the test.zip file recursively
$result = rsearch($_SERVER['DOCUMENT_ROOT'], '/.*\/test\.zip/'));
var_dump($result);
?>

RecursiveDirectoryIterator 是 PHP5 自带的,而 glob 则是从 PHP4 开始就有了。两者都可以完成工作,取决于您的选择。


2
好的,但是我该如何使用它在文件夹/子文件夹/子子文件夹中搜索特定文件并返回文件路径? - Winston Smith
7
rsearch:var_dump(rsearch('/folder/.../', '/.*zip/'));该代码将在文件系统中搜索以"/folder/"为根目录的所有子目录中名字包含"zip"后缀的文件,并将结果输出为一个数组。rglob:var_dump(rglob('/folder/*/test.zip'));该代码将在以"/folder/"为根目录的所有直接子目录中搜索名为"test.zip"的文件,并将结果输出为一个数组。 - Tony Chen
1
我对一个深层目录结构进行了一些轶事测试,发现搜索功能快了一个数量级... - JasonRDalton
2
@JasonRDalton,我重新测试了一下,使用PHP 7.1(“顺便说一句”),在一个60MB的项目树上(带有两个中等大小的git工作树和许多其他小文件等),结果完全相反。在测量之前为两者都进行了预热运行,而且我得到的数字非常一致,如下:rglob: 0.02864,rsearch: 0.12413。实际上,这比另一种方式更合理,我想说。 - Sz.
1
我建议您编辑您的答案,包括 rsearch($root, '/.*\/test.zip/'));,以节省新手浪费大量时间扫描默认情况下不可见的评论。除此之外,这是一个很好的答案。 - Wonko the Sane
显示剩余12条评论

40

我希望为那些可以预测最大深度的情况提供另一种简单的选择。您可以使用带有大括号的模式,列出所有可能的子文件夹深度。

此示例允许任意的0-3个子文件夹:

glob("$root/{,*/,*/*/,*/*/*/}test_*.zip", GLOB_BRACE);
当然,括号模式可以通过程序生成。

请注意,GLOB_BRACE并非所有平台都可用。我在自动化流水线中发现我的代码失败时才意识到这一点。 - coatesap
或者对于多个文件类型(例如 .pdf、.mp4 和 .mp3): glob("$root/{.pdf,/.pdf,//.pdf,///.pdf,.mp4,/.mp4,//.mp4,///.mp4,.mp3,/.mp3,//.mp3,///.mp3}", GLOB_BRACE) - HosseinNedaee
@HosseinNedaee 多种类型也可以用大括号模式表示:"$root/{,*/,*/*/,*/*/*/}test_*.{zip,gz,tgz}" - Pinke Helga

11

这将返回文件的完整路径

function rsearch($folder, $pattern) {
    $iti = new RecursiveDirectoryIterator($folder);
    foreach(new RecursiveIteratorIterator($iti) as $file){
         if(strpos($file , $pattern) !== false){
            return $file;
         }
    }
    return false;
}

调用函数:

$filepath = rsearch('/home/directory/thisdir/', "/findthisfile.jpg");

这将返回类似于:

/home/directory/thisdir/subdir/findthisfile.jpg

您可以改进此函数以查找多个文件,例如所有JPEG文件:

function rsearch($folder, $pattern_array) {
    $return = array();
    $iti = new RecursiveDirectoryIterator($folder);
    foreach(new RecursiveIteratorIterator($iti) as $file){
        if (in_array(strtolower(array_pop(explode('.', $file))), $pattern_array)){
            $return[] = $file;
        }
    }
    return $return;
}

这可以被称为:

$filepaths = rsearch('/home/directory/thisdir/', array('jpeg', 'jpg') );

参考:https://dev59.com/XXI-5IYBdhLWcg3wbXtN#1860417


4
为避免出现“PHP Notice: Only variables should be passed by reference in ...”的提示,可能应该使用$file->getExtension()而不是array_pop(explode('.', $file))。请注意保持原意并使翻译通俗易懂。 - Simon Nuttall
@Sadee 感谢你的函数,它在我的项目中运行良好。我唯一想添加的是,在文件夹路径不存在的情况下加入一个 die,以便不会继续执行。 - Michael Rogers
2
你可能想使用 yield 而不是构建一个完整的 $return 数组。这将产生一个 generator 并大大提高性能。 - alexandre-rousseau

7
作为解决您问题的完整方案(这也是我的问题):
<?php
function rsearch($folder, $pattern) {
    $dir = new RecursiveDirectoryIterator($folder);
    $ite = new RecursiveIteratorIterator($dir);
    $files = new RegexIterator($ite, $pattern, RegexIterator::MATCH);


    foreach($files as $file) {
         yield $file->getPathName();
    }
}

这将为您获取您想要查找的项目的完整路径。

编辑:感谢Rousseau Alexandre指出,$pattern必须是正则表达式。


模式必须是一个正则表达式 - alexandre-rousseau
你能简要举个例子说明如何调用它并迭代结果吗? - mwfearnley
获取所有扩展名为.html的文件的示例模式:$pattern = '#^.*\.html$#' - Abdull

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