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

PHP 將圖片存入 MySQL 的多種方案與效能比較

     編輯:PHP 相關知識

前言:是否應將圖片存入資料庫?

一般建議:不要。將圖片存入資料庫會導致:

  • 資料庫體積暴增,備份/還原時間大幅增加。
  • 記憶體使用率飆升,影響查詢效能。
  • 無法直接利用 CDN、瀏覽器快取、Web 伺服器靜態檔案優化。

最佳實務:圖片存檔案系統/物件儲存(S3、GCS、MinIO),資料庫僅存路徑/URL/雜湊值。

若因特殊需求(如嚴格交易一致性、小型封閉系統、遷移困難)必須存入 DB,本文提供完整方案比較。

資料表結構設計

CREATE TABLE `images` (
    `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
    `filename` VARCHAR(255) NOT NULL,
    `mime_type` VARCHAR(100) NOT NULL,
    `size` INT UNSIGNED NOT NULL,
    `data` LONGBLOB NOT NULL,          -- 二進位資料
    `checksum` CHAR(64) NOT NULL,      -- SHA256 校驗和
    `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    KEY `idx_checksum` (`checksum`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

關鍵點:使用 LONGBLOB(4GB 上限),加上 checksum 可快速去重。

方案比較:四種編碼方式實測

測試環境:PHP 8.2、MySQL 8.0、17KB JPEG 圖片。

方法 儲存大小 讀取解碼 優點 缺點
PMA_sqlAddslashes (phpMyAdmin 內部函式) 12.8 KB (最小) 無需解碼 體積最小、相容 phpMyAdmin 匯出 需複製 phpMyAdmin 程式碼、非標準
Base64 Encode 17 KB (+33%) base64_decode() 標準化、純文字、可直接嵌入 HTML data URI 體積膨脹 33%、CPU 額外開銷
Bin2Hex 25 KB (+100%) MySQL UNHEX() 或 PHP hex2bin() 純十六進位、SQL 可直接寫入 0x... 體積翻倍、效能最差
Prepared Statement + 參數綁定 (推薦) 12.8 KB (原始) 無需編碼 最安全、效能最好、無體積膨脹、防 SQL 注入 需使用 PDO/mysqli 預處理

推薦方案:PDO 預處理語句(零編碼、最安全)

<?php
$dsn = "mysql:host=localhost;dbname=test;charset=utf8mb4";
$pdo = new PDO($dsn, 'root', 'password', [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);

// 上傳處理
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['image'])) {
    $file = $_FILES['image'];
    
    // 基本驗證
    $allowedMimes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
    if (!in_array($file['type'], $allowedMimes, true)) {
        die("不支援的檔案類型");
    }
    if ($file['size'] > 5 * 1024 * 1024) { // 5MB 限制
        die("檔案過大");
    }
    if ($file['error'] !== UPLOAD_ERR_OK) {
        die("上傳錯誤:" . $file['error']);
    }

    // 讀取二進位資料
    $binaryData = file_get_contents($file['tmp_name']);
    $checksum = hash('sha256', $binaryData);

    // 檢查去重
    $stmt = $pdo->prepare("SELECT id FROM images WHERE checksum = ?");
    $stmt->execute([$checksum]);
    if ($existing = $stmt->fetch()) {
        echo "圖片已存在,ID: {$existing['id']}";
        exit;
    }

    // 寫入資料庫(參數綁定自動處理二進位,無需編碼)
    $stmt = $pdo->prepare(
        "INSERT INTO images (filename, mime_type, size, data, checksum) VALUES (?, ?, ?, ?, ?)"
    );
    $stmt->bindParam(1, $file['name'], PDO::PARAM_STR);
    $stmt->bindParam(2, $file['type'], PDO::PARAM_STR);
    $stmt->bindParam(3, $file['size'], PDO::PARAM_INT);
    $stmt->bindParam(4, $binaryData, PDO::PARAM_LOB); // 關鍵:LOB 型別
    $stmt->bindParam(5, $checksum, PDO::PARAM_STR);
    $stmt->execute();

    $id = $pdo->lastInsertId();
    echo "上傳成功,ID: $id";
}

// 讀取並輸出圖片
if (isset($_GET['id'])) {
    $stmt = $pdo->prepare("SELECT mime_type, data FROM images WHERE id = ?");
    $stmt->execute([$_GET['id']]);
    $img = $stmt->fetch(PDO::FETCH_ASSOC);
    
    if ($img) {
        header("Content-Type: {$img['mime_type']}");
        header("Content-Length: " . strlen($img['data']));
        header("Cache-Control: public, max-age=31536000");
        echo $img['data'];
    } else {
        http_response_code(404);
        echo "Not Found";
    }
}
?>

前端上傳表單範例

<form action="upload.php" method="post" enctype="multipart/form-data">
    <input type="hidden" name="MAX_FILE_SIZE" value="5242880">
    <input type="file" name="image" accept="image/jpeg,image/png,image/gif,image/webp" required>
    <button type="submit">上傳</button>
</form>

大檔案處理:分塊讀寫避免記憶體耗盡

若圖片超過 PHP memory_limit,使用資料流分塊寫入:

// 寫入大型 Blob(需 PDO::ATTR_STRINGIFY_FETCHES => false)
$pdo->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false);
$stmt = $pdo->prepare("INSERT INTO images (data) VALUES (?)");
$fp = fopen($file['tmp_name'], 'rb');
$stmt->bindParam(1, $fp, PDO::PARAM_LOB);
$stmt->execute();
fclose($fp);

效能優化建議

  1. 啟用壓縮:MySQL 8.0+ 支援 COMPRESS() / UNCOMPRESS(),或應用層用 zstd/zlib 壓縮後再存入。
  2. 分離資料表:將 data 欄位移至獨立資料表 image_blobs,主表只存中繼資料,避免全表掃描時讀取大量 Blob。
  3. 快取層:熱門圖片加 Redis/Memcached 快取,減少 DB 壓力。
  4. 非同步處理:上傳先存暫存區,背景工作佇列處理壓縮、縮圖、寫入 DB。

phpMyAdmin 相容性說明

若需相容 phpMyAdmin 匯出/匯入 SQL 檔,可參考其 PMA_sqlAddslashes 邏輯,但強烈建議改用參數綁定,現代資料庫驅動已完美支援二進位參數,無需手動轉義。

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