后臺站點文件掃描

前言

這幾天在看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函數的時候務必明確這一點,傳正確的參數?。。?/p>

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函數,最后封裝成了一個通用的類,效果還不錯。

缺點嘛,顯而易見。代碼的風格不是很好,命名什么的也是按照我自己的套路來的,不是很正規。

其他的貌似也沒什么了,那就先這樣好了。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,324評論 25 708
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,933評論 18 139
  • linux資料總章2.1 1.0寫的不好抱歉 但是2.0已經改了很多 但是錯誤還是無法避免 以后資料會慢慢更新 大...
    數據革命閱讀 12,228評論 2 33
  • 冰箱里面還放著去年不知道什么節日你送的一盒M&M's,各種粉紅色粉藍色粉紫色的巧克力豆,我一粒也沒舍得吃,因為...
    Miss_Cakecake閱讀 355評論 0 0
  • 今日同家人在外用餐,期間有小游戲,欣然前往參與。一路過關斬將,卻在緊要關頭,一時失誤,與大獎失之交臂。由此我想到了...
    落葉戀風zt閱讀 453評論 0 1