后台站点文件扫描

前言

这几天在看easyui,看到树形结构这个组件的时候突发奇想,能不能把站点以目录树的形式展示呢?

然后着手实现了一下,具体的来说是实现了对数据层的获取,还没有附加到tree组件上。下面就来谈谈我对这次文件信息抓取的体会吧。

遍历文件

在PHP中遍历文件有很多方式,但是适用的场景不尽相同。所以在合适的场合适用合适的方法显得至关重要,下面简要的了解一下。

scandir

如果说想找到一款类似于Python中使用os.walk获取文件目录信息的优雅的方法,在PHP中就不是那么的方便了,唯一能称得上简单的就是scandir函数,但是这个函数并不优雅,其作用就是扫描给定目录下的文件信息(如果包含子目录,那就只能显示到子目录的层级,再想查看子目录下的信息,那就不行了,否则会报错的)。

空口无凭,找个实例来看一下就一目了然了。

给定目录

<?php
/**
 * Created by PhpStorm.
 * User: ${郭璞}
 * Date: 2017/2/3
 * Time: 20:25
 * Description: scandir函数测试
 */

$pathinfo = scandir('.');
var_dump($pathinfo);

效果如下


正常解析目录

非法使用

所谓非法使用,就是指给出非目录文件时的场景,比如我们直接给个文件的路径,就是这样了。

<?php
/**
 * Created by PhpStorm.
 * User: ${郭璞}
 * Date: 2017/2/3
 * Time: 20:25
 * Description: scandir函数测试
 */

$pathinfo = scandir('./scandir.php');
var_dump($pathinfo);

非法使用场景

所以,使用scandir函数的时候务必明确这一点,传正确的参数!!!

dir函数

既然使用scandir函数不行了,那咱们就换个思路呗。下面介绍一个比较常用的方法。

<?php
/**
 * Created by PhpStorm.
 * User: ${郭璞}
 * Date: 2017/2/3
 * Time: 20:25
 * Description: scandir函数测试
 */

$path = ".";
if(is_dir($path)) {
    $dirinfo = dir($path);
    while($file = $dirinfo->read()) {
        echo "<mark>".$file."</mark><br />";
    }
    $dirinfo->close();
}else if (is_file($path)) {
    echo "<font color='green'>".$path."</font>";
}

获取指定目录下文件信息

如果将$path='.'换成$path='./scandir.php'。将出现如下结果。

dir函数处理非目录信息

当然,这两个方法都没能实现我们想要的效果。不能突破子目录的情况,没办法遍历到最底层的文件信息。

递归法

既然如此,那就得另寻他法了。我个人觉得递归的方法不赖,应该可以灵活地处理这些问题,说做就做。

使用面向过程的PHP编码方法需要处理外部数组引用问题,显得代码不是很容易理解,所以我选择面向对象的方法,将外部数组封装到一个类中,专门用于处理这类问题。

<?php
/**
 * Created by PhpStorm.
 * User: ${郭璞}
 * Date: 2017/2/3
 * Time: 9:32
 * Description: 读取给定目录及子目录下文件路径信息
 */

class FileWatcher{
    public $fileinfo;
    /**
     * FileWatcher constructor.
     * @param $path 给定路径
     */
    function __construct(){
        $this->fileinfo = array();
    }

    /**
     * 去除路径设置信息,析构方法
     */
    function __destruct()
    {
        // TODO: Implement __destruct() method.
        $this->fileinfo = null;
    }

    public function scanDir($path) {
        if(is_dir($path)) {
            $tmpdir = dir($path);
            while($tmpfile = $tmpdir->read()) {
                if($tmpfile!='.' && $tmpfile!='..')
                    $this->scanDir($path."/".$tmpfile);
            }
            $tmpdir->close();
        }
        if(is_file($path)) {
            array_push($this->fileinfo, $path);
        }
        return $this->fileinfo;
    }


}

下面是测试时使用的代码。

$fileWatcher = new FileWatcher();
$result = $fileWatcher->scanDir('.');

var_dump($result);

最终实现的效果为:


递归效果实现目录遍历

路径解析

单单是这样,不是很好用。我就想着能不能实现类似于Python中os.walk那样优雅的获取相关的信息呢?

数据结构设计

使用过那个方法的应该都了解,获取到的元组信息非常的详细,包括路径啊,目录级啊什么的非常的详细。

但是我这边为了以后使用easyui的tree组件,可能需要处理一下目录深度的问题,所以我设计了下面的数据结构。比较简单,但是实用性感觉还是挺强的。

class FileInfo{
    // 目录深度
    public $level;
    // 文件经过的路径,以数组形势依次填充
    public $pathstep;
    // 文件的完整路径
    public $fullpath;

    public function __construct()
    {
        //pathstep 存储当前路径经过的文件夹信息
        $this->pathstep = array();
    }

    public function __destruct()
    {
        // TODO: Implement __destruct() method.
        $this->pathstep = null;
        $this->level = null;
        $this->fullpath = null;
    }

}

原理解析

我个人认为原理还是比较简单的了,那就是以文件分隔符作为计算标准。当然了,需要处理一大堆的路径适配问题,尤其是./../这样的相对路径。

处理完这些之后就轻松多了,使用explode函数将字符串进行分割,装填到数组中即可。

代码实现

class PathParser{
    private $patharray;

    private $resultSet;


    public function __construct($patharray)
    {
        // 从外部获取到处理结果集
        $this->patharray = $patharray;
        // 初始化结果集数组
        $this->resultSet = array();
        // bean类对象
        $this->fileinfo = new FileInfo();
    }

    public function __destruct()
    {
        // TODO: Implement __destruct() method.
        $this->resultSet = null;
        $this->level = null;
        $this->fullpath = null;
    }

    public function parse() {
        for ($index=0; $index<count($this->patharray); $index++) {
            // 赋予完整路径
            $fileinfo = new FileInfo();
            $fileinfo->fullpath = $this->patharray[$index];
            //计算level
            $fileinfo->level = $this->parseLavel($fileinfo->fullpath);
//            echo $fileinfo->level."<------->";

            // 计算经过的路径并进行存储
            $fileinfo->pathstep = $this->parseStep($fileinfo->fullpath);
//            echo $fileinfo->pathstep."<br />";

//            var_dump($fileinfo->pathstep);

            array_push($this->resultSet, $fileinfo);


        }
        //返回计算结果,整体作为结果集返回
        return $this->resultSet;
    }

    /**
     * 获取给定路径所经过的路径的结果集,将用于分级目录展示
     * @param $fileinfo
     * @return int
     */
    public function parseStep($fileinfo) {
        if(!$fileinfo) {
            echo "<mark>".$fileinfo." path error!</mark>";
            exit();
        }
        // 判断是否为相对路径是的话去掉第一级目录。 啊好烦,windows上和linux上差别还这么大,怎么处理好呢。。。
        // 还是按照文件在服务器上的位置来进行来处理好了。判断是不是相对路径然后再针对“路径分隔符”计算路径的level
        if($this->isRelativePath($fileinfo) == 1) {
            // 相对路径处理
            // 去掉相对路径符号
            $fileinfo = substr($fileinfo,2, strlen($fileinfo));

            // 按照目录分隔符 作为切割标准,结果就是路径本身包含的路径信息
            return explode("/", $fileinfo);
        }else if($this->isRelativePath($fileinfo) == 2){
            $fileinfo = substr($fileinfo, 3, strlen($fileinfo));
            return explode("/", $fileinfo);
        }else if ($this->isAbsolutePath($fileinfo)) {
            // 绝对路径处理
        }else{
            // 文件路径非法
            echo "<mark>".$fileinfo." 文件路径非法</mark>";
            exit();
        }
    }

    public function parseLavel($fileinfo) {
        if(!$fileinfo) {
            echo "<mark>".$fileinfo." path error!</mark>";
            exit();
        }
        //按照文件在服务器上的位置来进行来处理好了。判断是不是相对路径然后再针对“路径分隔符”计算路径的level
        if($this->isRelativePath($fileinfo) == 1) {
            // 相对路径处理
            // 去掉当前相对路径符号
            $fileinfo = substr($fileinfo,2, strlen($fileinfo));
            // 通过计算 路径分隔符来作为level的判断标准
//            echo "<mark>".count(explode("/", $fileinfo))."</mark>";
            return count(explode("/", $fileinfo));
        }else if ($this->isRelativePath($fileinfo) == 2 ) {
            //去掉父级目录信息
            $fileinfo = substr($fileinfo, 3, strlen($fileinfo));
            return count(explode("/", $fileinfo));
        }else if ($this->isAbsolutePath($fileinfo)) {
            // 绝对路径处理
            // 算了,先不做这块了,貌似偏离了我这个需求。
        }else{
            // 文件路径非法
            echo "<mark>".$fileinfo." 文件路径非法</mark>";
            exit();
        }


    }

    /**
     * 判断是否为相对路径
     * @param $path
     * @return bool
     *
     */
    private function isRelativePath($path) {
        // 父级目录拥有更高的优先级
        $prefix = substr($path, 0, 3);
        if($prefix == "../") {
            return 2;
        }

        // 处理 当前目录情况
        $prefix = substr($path, 0, 2);
        if ($prefix == "./"){
            return 1;
        }else{
            return false;
        }
    }

    /**
     * 判断给定路径是否为绝对路径
     * @param $path
     * @return bool
     */
    private function isAbsolutePath($path) {
        $prefix = substr($path, 0, 1);
        if($prefix=="/"){
            return true;
        }else{
            return false;
        }
    }


}

演示

下面演示一下实现的效果吧。

当前目录

测试代码如下


//获取全部文件以及路径信息
$fileWatcher = new FileWatcher();
$result = $fileWatcher->scanDir('.');


$pathParser = new PathParser($result);
$resultSet = $pathParser->parse();
echo json_encode($resultSet);

结果图


当前目录信息获取

父级目录

对于父级目录信息获取,也是非常方便的。之前网上下载了easyui的压缩包,解压后扔到了apache服务器上,下面来看看对这个大文件信息集的获取情况吧。
测试代码把路径中的那个.改成../easyui即可。

父级目录信息获取

结果还行吧。我看着挺详细的了。那么到这里就差不多实现预期的效果了。

总结

回顾一下,今天主要是对于文件目录信息的遍历。

显示通过通用的scandir 函数和dir循环读取方式对目录进行了读取,但是效果不佳,于是转战递归实现。

为了达到一个更加优雅的信息获取效果,又设计了一个专门针对于文件的类,用于存储相关数据。

为了处理相对路径中本级目录和父级目录等特殊情况,又使用了substr和explode函数,最后封装成了一个通用的类,效果还不错。

缺点嘛,显而易见。代码的风格不是很好,命名什么的也是按照我自己的套路来的,不是很正规。

其他的貌似也没什么了,那就先这样好了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,290评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,399评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,021评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,034评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,412评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,651评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,902评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,605评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,339评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,586评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,076评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,400评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,060评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,851评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,685评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,595评论 2 270

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,569评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • linux资料总章2.1 1.0写的不好抱歉 但是2.0已经改了很多 但是错误还是无法避免 以后资料会慢慢更新 大...
    数据革命阅读 12,018评论 2 34
  • 冰箱里面还放着去年不知道什么节日你送的一盒M&M's,各种粉红色粉蓝色粉紫色的巧克力豆,我一粒也没舍得吃,因为...
    Miss_Cakecake阅读 314评论 0 0
  • 今日同家人在外用餐,期间有小游戏,欣然前往参与。一路过关斩将,却在紧要关头,一时失误,与大奖失之交臂。由此我想到了...
    落叶恋风zt阅读 401评论 0 1