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

PHP 圖片管理系統:數據庫與文件系統雙重刪除實戰

     編輯:PHP 相關知識

項目背景與需求分析

在內容管理系統(CMS)或電商後台中,圖片管理是核心功能之一。用戶上傳圖片時,通常會在數據庫記錄元數據(名稱、路徑、上傳時間等),同時將文件存儲在服務器磁盤上。刪除操作必須保證數據庫記錄與物理文件同步移除,否則會產生「孤兒文件」佔用存儲,或「斷鏈記錄」導致前端報錯。本文通過一個完整的後台管理頁面案例,演示如何實現分頁列表、單條刪除、批量操作的最佳實踐。

完整代碼架構

該頁面採用「PHP + HTML 混合輸出」的傳統模式,包含三大模塊:

  1. 刪除處理邏輯:優先查詢路徑,再刪除數據庫,最後 unlink 物理文件
  2. 分頁查詢邏輯:計算總條數、總頁數、偏移量,執行 LIMIT 查詢
  3. 前端渲染模板:表格展示、操作按鈕、分頁導航條
<?php
/**
 * 圖片管理後台 - listUpload.php
 * 功能:列表展示、分頁、單條/批量刪除(數據庫+文件)
 */
include "isLogin.php"; // 權限驗證
?>
<!DOCTYPE html>
<html>
<head>
    <meta charset="gb2312">
    <title>後台管理 - 圖片管理</title>
    <link rel="stylesheet" href="style/lampcms.css">
    <script src="javascript/common.js"></script>
</head>
<body>
<div id="main">
    後台管理 → 圖片管理
    <form method="post" action="">
    <div class="msg-box">
        <ul class="viewmess">
            <li class="dark-row">
                <span class="list_width width_font">圖片名稱</span>
                <span class="list_width width_font">圖片路徑</span>
                <span class="list_width width_font">添加時間</span>
                <span class="list_width width_font">操作</span>
            </li>

            <?php
            require "../conn/conn.php";

            // ===== 1. 處理刪除請求(GET 方式,生產環境建議改 POST + CSRF)=====
            if (isset($_GET['del'])) {
                $id = intval($_GET['del']); // 強制轉整型,防注入
                // 先查路徑
                $sql2 = "SELECT picpath FROM pic WHERE id = $id";
                $rst2 = mysql_query($sql2);
                $row = mysql_fetch_assoc($rst2);

                // 刪除數據庫記錄
                mysql_query("DELETE FROM pic WHERE id = $id");

                // 刪除物理文件
                if ($row && file_exists($row['picpath'])) {
                    unlink($row['picpath']);
                } elseif ($row) {
                    echo "<script>alert('圖片路徑不正確:{$row['picpath']}');</script>";
                }
            }

            // ===== 2. 分頁參數計算 =====
            $sql = "SELECT COUNT(*) AS total FROM pic";
            $totalnum = mysql_result(mysql_query($sql), 0, 'total');
            $pagenum = 5; // 每頁條數
            $page = isset($_GET['page']) ? max(1, intval($_GET['page'])) : 1;
            $begin = ($page - 1) * $pagenum;
            $pagecount = ceil($totalnum / $pagenum);

            // ===== 3. 當前頁數據查詢 =====
            $sql2 = "SELECT * FROM pic ORDER BY id DESC LIMIT $begin, $pagenum";
            $rst2 = mysql_query($sql2);
            while ($row = mysql_fetch_assoc($rst2)) {
            ?>
                <li class="light-row" style="padding: 2px 0;">
                    <span class="list_width width_font"><a href="" target="_blank"><?php echo htmlspecialchars($row['picname']); ?></a></span>
                    <span class="list_width width_font"><?php echo htmlspecialchars($row['picpath']); ?></span>
                    <span class="list_width width_font"><?php echo $row['ctime']; ?></span>
                    <span class="list_width width_font">
                        <a href="editUpload.php?edit=<?php echo $row['id']; ?>">【修改】</a>
                        <a href="listUpload.php?del=<?php echo $row['id']; ?>" onclick="return confirm('確定刪除此圖片?')">【刪除】</a>
                    </span>
                </li>
            <?php } ?>

            <!-- 分頁導航 -->
            <li class="dark-row">
                <span class="right">
                    共 <?php echo $totalnum; ?> 條 ;共 <?php echo $pagecount; ?> 頁 ;每頁顯示 <?php echo $pagenum; ?> 條
                    <a href="<?php echo $_SERVER['PHP_SELF']; ?>?page=1">首頁</a>
                    <a href="<?php echo $_SERVER['PHP_SELF']; ?>?page=<?php echo max(1, $page-1); ?>">上一頁</a>
                    <a href="<?php echo $_SERVER['PHP_SELF']; ?>?page=<?php echo min($pagecount, $page+1); ?>">下一頁</a>
                    <a href="<?php echo $_SERVER['PHP_SELF']; ?>?page=<?php echo $pagecount; ?>">尾頁</a>
                </span>
            </li>
        </ul>
    </div>
    </form>
</div>
</body>
</html>

核心邏輯深度剖析

1. 刪除操作的原子性與順序

代碼採用「先查路徑 → 刪數據庫 → 刪文件」的順序,這是一個關鍵設計決策:

  • 先查路徑:必須在數據庫記錄存在時讀取 picpath,否則刪除記錄後無法知道文件位置。
  • 先刪數據庫:若文件刪除失敗(權限不足、文件被佔用),數據庫已無記錄,避免用戶界面顯示「已刪除」但文件殘留的不一致狀態。日誌記錄失敗信息供運維排查。
  • 事務擴展建議:若使用 InnoDB,可將「刪記錄 + 寫操作日志」包裹在事務中,文件刪除失敗時回滾事務,實現真正的原子性。

2. 分頁算法的邊界處理

$page = isset($_GET['page']) ? max(1, intval($_GET['page'])) : 1;
// 防止負數、非數字、超大頁碼
$page = min($page, max(1, $pagecount));

上述代碼通過 intval() 強制類型轉換,max()/min() 限制範圍,徹底杜絕 SQL 注入與邏輯越界。

3. 安全性加固清單

風險點原代碼問題修復方案
SQL 注入直接拼接 $id 進 SQLintval($_GET['del']) 或預處理語句
XSS 攻擊直接輸出 $row['picname'] 等字段htmlspecialchars() 轉義輸出
CSRF 攻擊刪除操作用 GET 請求,無 Token 驗證改為 POST + 隱藏 csrf_token 驗證
路徑遍歷未驗證 picpath 是否在允許目錄realpath() 對比 UPLOAD_ROOT 確保在沙箱內

現代化重構建議

若項目允許引入現代框架(Laravel/ThinkPHP/Symfony)或組件,建議:

  1. PDO 預處理:替換 mysql_* 函數(已棄用/移除)。
  2. 服務層解耦:將刪除邏輯封裝為 PictureService::delete($id),控制器僅處理請求響應。
  3. 異步文件刪除:大文件或雲存儲(OSS/S3)場景下,將 unlink 推入消息隊列,避免阻塞 HTTP 響應。
  4. 前後端分離:API 返回 JSON,前端 Vue/React 渲染表格與分頁組件。

總結

本案例展示了經典 PHP 圖片管理的核心流程:數據庫與文件系統的雙重刪除、分頁查詢、前端交互。雖然代碼風格屬於早期「頁面級腳本」模式,但其業務邏輯清晰,是理解 Web 文件管理本質的優秀教材。實際項目中,請務必在安全性、可維護性、擴展性三個維度進行分層重構,構建健壯的生產級系統。

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