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

PHP 開發類 DedeCMS 風格檔案管理系統 (含上傳、刪除、多目錄)

     編輯:PHP 相關知識

專案背景與功能概覽

許多 CMS (如 DedeCMS) 內建檔案管理器,支援圖片/檔案上傳、目錄瀏覽、檔案刪除、重命名衝突處理,並能整合編輯器 (CKEditor、FCKeditor) 回填路徑。本文架構一個輕量級 PHP 檔案管理模組,採用單一進入點、Action 分發模式,核心功能包括:

  • 多層目錄瀏覽 (左側樹狀 / 右側列表)
  • 檔案上傳 (支援白名單副檔名、大小限制、同名自動重命名 file(1).jpg)
  • 檔案/目錄刪除
  • 編輯器整合模式 (回傳檔案名或圖片標籤給父視窗)

核心架構:單一進入點與 Action 路由

<?php
// config.php - 集中配置
define('ROOT', str_ireplace('/manager/' . basename(__FILE__), '', str_replace('\\', '/', __FILE__)));
define('UPLOAD_ROOT', '/uploadfile/');                 // 相對網站根目錄
define('ALLOWED_TYPES', 'gif|jpg|png|txt|doc|xls|ppt|pdf|rar|zip|7z|chm|mp3|mp4|mpg|mpeg|flv|swf');
define('MAX_SIZE', ini_get('upload_max_filesize'));    // 讀取 php.ini 設定

// 依據 act 參數分發
$act = $_GET['act'] ?? '';

switch ($act) {
    case '':       renderFrame(); break;      // 主框架 (含左側目錄樹、右側 iframe)
    case 'left':   renderLeftTree(); break;   // 左側目錄樹
    case 'right':  handleRightPanel(); break; // 右側列表/上傳/刪除邏輯
    default:       http_response_code(404); exit('Invalid Action');
}

function renderFrame() {
    // 輸出 frameset 或現代 layout (建議使用 CSS Grid/Flex + iframe)
    // 左側 src="?act=left",右側 src="?act=right"
}
?>

右側面板核心邏輯

function handleRightPanel() {
    // 1. 處理上傳 POST 請求
    if (!empty($_POST['upfolder']) && $_SERVER['REQUEST_METHOD'] === 'POST') {
        uploadFile();
        exit;
    }

    $dir = $_GET['folder'] ?? '';
    $targetDir = $dir === '' ? UPLOAD_ROOT : $dir;

    // 2. 處理刪除
    if (($del = $_GET['del_file'] ?? '') !== '') {
        deleteFile($targetDir, $del);
        // 刪除後重導回列表
        header("Location: ?act=right&folder=" . urlencode($dir));
        exit;
    }

    // 3. 渲染檔案列表
    renderFileList($targetDir, $dir);
}

關鍵功能實作細節

1. 檔案上傳

function uploadFile() {
    $upDir = ROOT . UPLOAD_ROOT . ($_POST['upfolder'] ?? '');
    if (!is_dir($upDir)) mkdir($upDir, 0755, true);

    $file = $_FILES['newfile'] ?? null;
    if (!$file || $file['error'] !== UPLOAD_ERR_OK) {
        exit(json_encode(['error' => '上傳失誤或無檔案']));
    }

    // 副檔名驗證 (雙重防護)
    $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
    if (!preg_match('/^(' . ALLOWED_TYPES . ')$/i', $ext)) {
        exit(json_encode(['error' => '禁止的檔案類型']));
    }
    // MIME 類型二次驗證 (建議使用 finfo)
    $finfo = new finfo(FILEINFO_MIME_TYPE);
    $mime = $finfo->file($file['tmp_name']);
    $allowedMimes = ['image/gif', 'image/jpeg', 'image/png', 'application/pdf', /* ... */];
    if (!in_array($mime, $allowedMimes)) {
        exit(json_encode(['error' => '檔案內容類型不符']));
    }

    // 檔案大小檢查
    if ($file['size'] > parseSize(MAX_SIZE)) {
        exit(json_encode(['error' => '檔案過大']));
    }

    // 衝突重命名: file.jpg -> file(1).jpg
    $baseName = pathinfo($file['name'], PATHINFO_FILENAME);
    $dest = $upDir . '/' . $file['name'];
    $counter = 1;
    while (file_exists($dest)) {
        $dest = $upDir . '/' . $baseName . '(' . $counter++ . ').' . $ext;
    }

    if (move_uploaded_file($file['tmp_name'], $dest)) {
        @chmod($dest, 0644);
        // 回傳給編輯器所需格式 (視編輯器 API 調整)
        exit(json_encode(['url' => UPLOAD_ROOT . basename($dest), 'name' => basename($dest)]));
    }
    exit(json_encode(['error' => '移動檔案失敗']));
}

function parseSize($str) { // 將 2M, 50K 轉換為位元組
    $unit = strtolower(substr($str, -1));
    $val = (int)substr($str, 0, -1);
    return match($unit) { 'k' => $val * 1024, 'm' => $val * 1024 * 1024, 'g' => $val * 1024 * 1024 * 1024, default => $val };
}

2. 目錄掃描與列表渲染

function renderFileList($absPath, $relPath) {
    $items = [];
    $dh = opendir($absPath);
    while (($file = readdir($dh)) !== false) {
        if ($file === '.' || $file === '..') continue;
        $full = $absPath . '/' . $file;
        $isDir = is_dir($full);
        $items[] = [
            'name' => $file,
            'is_dir' => $isDir,
            'size' => $isDir ? '-' : formatSize(filesize($full)),
            'mtime' => date('Y-m-d H:i', filemtime($full)),
            'url' => $relPath . '/' . $file, // 相對網站根目錄 URL
            'abs' => $full
        ];
    }
    closedir($dh);

    // 排序: 目錄優先,按名稱自然排序
    usort($items, fn($a, $b) => ($a['is_dir'] === $b['is_dir']) ? strnatcasecmp($a['name'], $b['name']) : ($b['is_dir'] ? 1 : -1));

    // 輸出 HTML (或 JSON 供前端 Vue/React 渲染)
    // 此處省略 HTML 模板,重點為資料結構
    // 刪除連結: ?act=right&folder={$relPath}&del_file={$file}
    // 進入子目錄: ?act=right&folder={$relPath}/{$file}
}
function formatSize($bytes) { /* ... */ }

3. 安全性強化清單 (必做項目)

風險對策
路徑穿越 (../etc/passwd)使用 realpath() 正規化路徑,嚴格檢查是否在 UPLOAD_ROOT 之下
腳本執行攻擊上傳目錄設定 Nginx/Apache 禁止 PHP 執行 (location ~ \.php$ { deny all; })
MIME 偽裝使用 finfo 檢測真實內容類型,不信任 $_FILES['type']
未授權存取整合 CMS 權限系統,驗證登入狀態與角色 (admin/editor)
CSRF上傳/刪除表單加入 Token 驗證

前端整合建議

現代專案建議採用 前後端分離:後端提供 RESTful API (GET /api/files?dir=..., POST /api/upload, DELETE /api/files/{id}),前端使用 Vue 3 + Element Plus 或 React + Ant Design 實作檔案管理器介面,支援拖曳上傳、多選刪除、右鍵選單、縮圖預覽等豐富交互。若需整合 CKEditor 5,可使用其官方 CKBox 或自訂 FileRepository 接口對接此 API。

結語

檔案管理模組看似簡單,實則涉及路徑安全、權限控管、編碼相容、前端整合等多層面向。採用「單一進入點 + Action 分發 + 服務層解耦」的結構,配合嚴格的輸入驗證與輸出編碼,可構建出安全且可維護的檔案管理核心,作為 CMS、後台系統、編輯器插件的堅實基礎設施。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved