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

PHP 圖片上傳完整實戰:從表單到資料庫存儲的最佳實踐

     編輯:PHP 綜合知識

架構概覽

完整上傳流程包含四個核心文件:

  1. conn.php - 資料庫連接配置
  2. upload.php - 表單頁面與業務邏輯
  3. uploadclass.php - 上傳核心類(驗證、重命名、存儲)
  4. SQL 結構 - 資料表定義

1. 資料庫連接 (conn.php)

<?php
$host = "localhost";
$user = "root";
$pwd  = "1721";
$conn = mysqli_connect($host, $user, $pwd); // 建議改用 mysqli/PDO

if (!$conn) {
    die("資料庫連接失敗:" . mysqli_connect_error());
}

$dbname = "database";
if (!mysqli_select_db($conn, $dbname)) {
    die("選擇資料庫失敗:" . mysqli_error($conn));
}

// 設定字符集
mysqli_set_charset($conn, 'utf8mb4');
?>
現代化建議:原代碼使用已棄用的 mysql_* 函數,務必升級為 mysqli 或 PDO,並使用預處理語句防止 SQL 注入。

2. 上傳核心類 (uploadclass.php)

<?php
class FileUploader {
    private $uploadDir = 'uploads/';      // 必須以 / 結尾
    private $allowedTypes = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
    private $maxSize = 2 * 1024 * 1024;   // 2MB
    private $prefix = 'IMG_';

    public function __construct($options = []) {
        foreach ($options as $key => $val) {
            if (property_exists($this, $key)) $this->$key = $val;
        }
        // 確保目錄存在
        if (!is_dir($this->uploadDir)) {
            mkdir($this->uploadDir, 0755, true);
        }
    }

    public function upload($fileInputName = 'file') {
        if (!isset($_FILES[$fileInputName]) || $_FILES[$fileInputName]['error'] !== UPLOAD_ERR_OK) {
            return ['success' => false, 'msg' => '無有效文件上傳或上傳出錯'];
        }

        $file = $_FILES[$fileInputName];
        
        // 檢查大小
        if ($file['size'] > $this->maxSize) {
            return ['success' => false, 'msg' => '文件超過大小限制 (' . ($this->maxSize/1024/1024) . 'MB)'];
        }

        // 檢查類型
        $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
        if (!in_array($ext, $this->allowedTypes)) {
            return ['success' => false, 'msg' => '不允許的文件類型,僅支援:' . implode(', ', $this->allowedTypes)];
        }

        // MIME 類型二次驗證(防偽裝)
        $finfo = finfo_open(FILEINFO_MIME_TYPE);
        $mime = finfo_file($finfo, $file['tmp_name']);
        finfo_close($finfo);
        $allowedMimes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
        if (!in_array($mime, $allowedMimes)) {
            return ['success' => false, 'msg' => '文件內容類型不合法'];
        }

        // 生成唯一文件名
        do {
            $filename = $this->prefix . date('YmdHis') . '_' . bin2hex(random_bytes(8)) . '.' . $ext;
            $destPath = $this->uploadDir . $filename;
        } while (file_exists($destPath));

        // 移動文件
        if (!move_uploaded_file($file['tmp_name'], $destPath)) {
            return ['success' => false, 'msg' => '文件移動失敗,請檢查目錄權限'];
        }

        // 設定權限
        chmod($destPath, 0644);

        return [
            'success' => true,
            'filename' => $filename,
            'path' => $destPath,
            'size' => $file['size'],
            'mime' => $mime
        ];
    }
}
?>

3. 處理頁面 (upload.php)

<?php
require_once 'conn.php';
require_once 'uploadclass.php';

$message = '';
$imagePreview = '';

if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'upload') {
    $title = trim($_POST['title'] ?? '');
    
    if ($title === '') {
        $message = '<div class="error">請輸入標題</div>';
    } else {
        $uploader = new FileUploader();
        $result = $uploader->upload('file');
        
        if ($result['success']) {
            // 使用預處理語句防注入
            $stmt = $conn->prepare("INSERT INTO upload (title, pic) VALUES (?, ?)");
            $stmt->bind_param("ss", $title, $result['path']);
            if ($stmt->execute()) {
                $message = '<div class="success">上傳成功!</div>';
                $imagePreview = '<img src="' . $result['path'] . '" alt="預覽" style="max-width:300px">';
            } else {
                $message = '<div class="error">資料庫寫入失敗:' . $stmt->error . '</div>';
                @unlink($result['path']); // 回滾文件
            }
            $stmt->close();
        } else {
            $message = '<div class="error">' . $result['msg'] . '</div>';
        }
    }
}
?>
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>圖片上傳</title>
<style>.success{color:green}.error{color:red} form{margin:20px 0}</style></head>
<body>
    <h2>圖片上傳示例</h2>
    
    
    <form method="post" action="" enctype="multipart/form-data">
        <input type="hidden" name="action" value="upload">
        <p>標題:<input type="text" name="title" required></p>
        <p>圖片:<input type="file" name="file" accept="image/*" required></p>
        <p><input type="submit" value="上傳"></p>
    </form>
    
    <div>

4. 資料表結構 (SQL)

CREATE TABLE `upload` (
    `uploadid` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
    `title` VARCHAR(100) NOT NULL DEFAULT '',
    `pic` VARCHAR(255) NOT NULL DEFAULT '',
    `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`uploadid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

關鍵安全加固清單

風險點防護措施
SQL 注入預處理語句
文件類型偽裝MIME 檢查 + 擴展名白名單
大文件攻擊配置 upload_max_filesize + 代碼層檢查
腳本執行上傳目錄禁用 PHP 執行(.htaccess: php_flag engine off)
文件名衝突隨機哈希 + 時間戳命名
路徑遍歷不使用用戶輸入作為路徑部分

進階擴展建議

  • 圖片壓縮/縮略圖:intervention/image 库
  • 雲存儲對接:AWS S3 / 阿里云 OSS /騰訊云 COS SDK
  • 異步上傳:Plupload / Dropzone.js + 進度條
  • 圖片審核:接入內容安全 API
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved