美文网首页首页推荐程序员
后台站点文件扫描

后台站点文件扫描

作者: 尽情的嘲笑我吧 | 来源:发表于2017-10-07 08:25 被阅读0次

    前言

    这几天在看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函数,最后封装成了一个通用的类,效果还不错。

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

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

    相关文章

      网友评论

        本文标题:后台站点文件扫描

        本文链接:https://www.haomeiwen.com/subject/csidyxtx.html