首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 無限級目錄瀏覽器:基於 GET 參數的動態目錄導航實現

     編輯:PHP 綜合知識

項目背景

構建一個類似文件管理器的 Web 界面,用戶可通過點擊目錄鏈接逐級深入瀏覽服務器文件系統。本文實現一個輕量級的無限級目錄瀏覽器,利用 chdir() 切換工作目錄,結合 opendir()/readdir() 讀取內容,通過 GET 參數傳遞當前路徑,實現無狀態的目錄導航。

核心實現代碼

<?php
/**
 * 無限級目錄瀏覽器
 * 安全提示:生產環境必須添加路徑白名單驗證,防止目錄遍歷攻擊
 */

// 設置編碼
header('Content-Type: text/html; charset=utf-8');

// 定義允許訪問的根目錄(安全基線)
$ALLOWED_ROOT = realpath(__DIR__ . '/public_files');
if (!$ALLOWED_ROOT) {
    die('配置錯誤:根目錄不存在');
}

// 獲取並驗證請求路徑
$requestPath = $_GET['path'] ?? '.';
$requestPath = urldecode($requestPath);

// 安全驗證:確保路徑在允許範圍內
$realPath = realpath($ALLOWED_ROOT . DIRECTORY_SEPARATOR . $requestPath);
if ($realPath === false || strpos($realPath, $ALLOWED_ROOT) !== 0) {
    die('非法路徑訪問');
}

// 切換工作目錄
chdir($realPath);
$currentDir = getcwd();

// 計算相對路徑(用於生成鏈接)
$relativePath = substr($realPath, strlen($ALLOWED_ROOT) + 1);
$relativePath = $relativePath === '' ? '.' : $relativePath;

// 讀取目錄內容
$entries = [];
$handle = opendir('.');
if ($handle) {
    while (($entry = readdir($handle)) !== false) {
        if ($entry === '.' || $entry === '..') continue;
        
        $fullPath = $entry; // 當前工作目錄下的相對路徑
        $isDir = is_dir($fullPath);
        
        $entries[] = [
            'name' => $entry,
            'is_dir' => $isDir,
            'path' => $relativePath === '.' ? $entry : $relativePath . '/' . $entry,
            'size' => $isDir ? '-' : filesize($fullPath),
            'modified' => date('Y-m-d H:i:s', filemtime($fullPath)),
        ];
    }
    closedir($handle);
}

// 排序:目錄優先,然後按名稱字母序
usort($entries, function($a, $b) {
    if ($a['is_dir'] !== $b['is_dir']) {
        return $b['is_dir'] ? -1 : 1; // 目錄在前
    }
    return strnatcasecmp($a['name'], $b['name']);
});

前端模板(HTML + 內嵌 CSS)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>目錄瀏覽器 - <?= htmlspecialchars($relativePath) ?></title>
    <style>
        body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; margin: 20px; }
        .breadcrumb { margin-bottom: 20px; padding: 10px; background: #f5f5f5; border-radius: 4px; }
        .breadcrumb a { margin-right: 8px; color: #0066cc; text-decoration: none; }
        .breadcrumb a:hover { text-decoration: underline; }
        table { width: 100%; border-collapse: collapse; }
        th, td { padding: 10px; text-align: left; border-bottom: 1px solid #eee; }
        th { background: #fafafa; font-weight: 600; }
        tr:hover { background: #f9f9f9; }
        .dir-name { font-weight: 500; color: #0066cc; }
        .file-size { color: #666; font-family: monospace; }
        .empty { text-align: center; color: #999; padding: 40px; }
        .back-link { display: inline-block; margin-bottom: 15px; color: #0066cc; }
    </style>
</head>
<body>
    <!-- 麵包屑導航 -->
    <div class="breadcrumb">
        <strong>當前位置:</strong>
        <a href="?path=">根目錄</a>
        <?php
        if ($relativePath !== '.') {
            $parts = explode('/', $relativePath);
            $buildPath = '';
            foreach ($parts as $part) {
                $buildPath .= ($buildPath ? '/' : '') . $part;
                echo ' / ' . htmlspecialchars($part) . '';
            }
        }
        ?>
    </div>

    <!-- 返回目錄鏈接 -->
    <?php if ($relativePath !== '.'): ?>
        <a href="?path=" class="back-link">← 返回上級目錄</a>
    <?php endif; ?>

    <!-- 文件列表 -->
    <?php if (empty($entries)): ?>
        <div class="empty">此目錄為空</div>
    <?php else: ?>
        <table>
            <thead>
                <tr>
                    <th>名稱</th>
                    <th>類型</th>
                    <th>大小</th>
                    <th>修改時間</th>
                </tr>
            </thead>
            <tbody>
                <?php foreach ($entries as $entry): ?>
                    <tr>
                        <td>
                            <?php if ($entry['is_dir']): ?>
                                <a href="?path=" class="dir-name">
                                    📁 
                                </a>
                            <?php else: ?>
                                📄 
                            <?php endif; ?>
                        </td>
                        <td>
                        <td class="file-size">
                            <?php 
                            if (!$entry['is_dir']) {
                                echo formatBytes($entry['size']);
                            }
                            ?>
                        </td>
                        <td>
                    </tr>
                <?php endforeach; ?>
            </tbody>
        </table>
    <?php endif; ?>

    <?php
    // 字節格式化輔助函數
    function formatBytes(int $bytes): string {
        $units = ['B', 'KB', 'MB', 'GB', 'TB'];
        $pow = floor(($bytes > 0 ? log($bytes) : 0) / log(1024));
        $pow = min($pow, count($units) - 1);
        return round($bytes / (1024 ** $pow), 2) . ' ' . $units[$pow];
    }
    ?>
</body>
</html>

關鍵技術點解析

1. 路徑安全防護(核心)

<?php
// 雙重驗證機制
$realPath = realpath($ALLOWED_ROOT . DIRECTORY_SEPARATOR . $requestPath);
// 1. realpath 解析符號鏈接、相對路徑,返回標準絕對路徑
// 2. strpos($realPath, $ALLOWED_ROOT) === 0 確保不越界

⚠️ 切勿直接使用用戶輸入路徑,必須經過 realpath() 規範化並驗證前綴。

2. 工作目錄切換策略

  • chdir($realPath) 將進程工作目錄切換到目標目錄
  • 後續 opendir('.')、is_dir()、filesize() 均基於當前工作目錄
  • getcwd() 獲取當前絕對路徑用於顯示

3. 相對路徑構建邏輯

將絕對路徑轉換為相對於根目錄的路徑,作為 GET 參數傳遞:

$relativePath = substr($realPath, strlen($ALLOWED_ROOT) + 1);
// 例如:/var/www/public_files/images/avatar.jpg -> images/avatar.jpg

功能擴展建議

功能實現思路
文件下載新增 download.php?path=...,使用 readfile() + Content-Disposition
圖片預覽判斷 MIME 類型,圖片直接 <img src="?path=...&preview=1">
新建目錄POST 表單提交,調用 mkdir(),需驗證權限
上傳文件enctype="multipart/form-data 表單,move_uploaded_file() 處理
重命名/刪除AJAX 請求,rename()/unlink()/rmdir(),需 CSRF 保護
搜索過濾前端 JS 過濾或後端 glob() 模糊匹配

生產環境部署清單

  • [ ] 設置 open_basedir 限制 PHP 訪問範圍
  • [ ] 配置 Nginx/Apache 禁用目錄瀏覽(防止繞過 PHP 腳本)
  • [ ] 添加身份認證(Basic Auth、Session、JWT 等)
  • [ ] 記錄訪問日誌(IP、路徑、操作類型、時間)
  • [ ] 設置目錄權限:目錄 755,文件 644,禁止腳本執行權限
  • [ ] 定期掃描異常文件(Webshell、大文件、可疑腳本)
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved