雖然生產環境通常建議將圖片存放檔案系統或雲端儲存 (僅存路徑於資料庫),但特定場景 (如小型應用、資料完整性要求高、避免檔案散落) 下將圖片以 BLOB 型別直接存入資料庫仍有其價值。本文提供完整實作範例。
CREATE TABLE IF NOT EXISTS `images` (
`id` INT(8) NOT NULL AUTO_INCREMENT,
`title` VARCHAR(150) DEFAULT NULL,
`content` LONGBLOB NOT NULL,
`mime_type` VARCHAR(50) NOT NULL,
`file_size` INT UNSIGNED NOT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
關鍵欄位說明:
LONGBLOB:支援最大 4GB 二進位資料 (MEDIUMBLOB 16MB, BLOB 64KB)mime_type:儲存 MIME 類型 (image/jpeg, image/png, image/gif 等),輸出時設定正確 Content-Typefile_size:儲存檔案大小,便於前端顯示或限制<?php
// 資料庫連線 (建議使用 PDO)
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'root', 'root', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]);if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['image'])) {
$file = $_FILES['image'];
// 基本驗證
$allowed_types = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
$max_size = 5 * 1024 * 1024; // 5MB
if (!in_array($file['type'], $allowed_types)) {
die('不支援的檔案類型');
}
if ($file['size'] > $max_size) {
die('檔案過大,限制 5MB');
}
if ($file['error'] !== UPLOAD_ERR_OK) {
die('上傳失敗,錯誤代碼: ' . $file['error']);
}
// 讀取檔案內容
$image_data = file_get_contents($file['tmp_name']);
if ($image_data === false) {
die('無法讀取檔案');
}
// 寫入資料庫 (使用預處理語句,避免 SQL 注入)
$sql = "INSERT INTO images (title, content, mime_type, file_size) VALUES (?, ?, ?, ?)";
$stmt = $pdo->prepare($sql);
$stmt->execute([
$file['name'],
$image_data,
$file['type'],
$file['size']
]);
echo "圖片上傳成功,ID: " . $pdo->lastInsertId();
}?>
<!DOCTYPE html>
<html>
<body>
<form method="post" enctype="multipart/form-data">
<input type="file" name="image" accept="image/*" required>
<button type="submit">上傳至資料庫</button>
</form>
</body>
</html>
<?php
// image.php?id=123
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'root', 'root');$id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
if ($id <= 0) {
http_response_code(400);
die('無效 ID');
}$stmt = $pdo->prepare("SELECT content, mime_type FROM images WHERE id = ?");
$stmt->execute([$id]);
$image = $stmt->fetch(PDO::FETCH_ASSOC);if (!$image) {
http_response_code(404);
die('圖片不存在');
}// 輸出正確的 HTTP 標頭
header('Content-Type: ' . $image['mime_type']);
header('Content-Length: ' . strlen($image['content']));
header('Cache-Control: public, max-age=31536000'); // 快取一年
header('ETag: "' . md5($image['content']) . '"');// 輸出二進位資料
echo $image['content'];
?>
<img src="image.php?id=123" alt="資料庫圖片">
<!-- 或作為 CSS 背景 -->
<div style="background-image: url(image.php?id=123); width: 200px; height: 200px;"></div>
| 優點 | 缺點 |
|---|---|
| 資料完整性:圖片與資料庫記錄原子性一致 | 資料庫體積暴增:備份/還原/複製耗時大幅增加 |
| 部署簡單:單一資料庫檔案即可遷移 | 記憶體壓力:PHP 處理大圖片需載入記憶體 |
| 權限控管:透過資料庫權限管理存取 | 無法利用 CDN/靜態檔案伺服器加速 |
| 交易支援:圖片與相關資料同步提交/回滾 | 複雜查詢效能:SELECT * 會載入大量 BLOB 資料 |
id, title, mime_type, file_size,不查 contentETag、Cache-Control、Expires