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

PHP 將圖片存入 MySQL 的正確姿勢:BLOB 儲存方案比較與最佳實踐

     編輯:PHP 綜合知識

雖然「圖片存檔案系統、資料庫只存路徑」是公認最佳實踐,但特定場景(如單檔案部署、資料庫同步備份、嚴格存取控制)仍需將圖片以 BLOB 形式存入 MySQL。本文實測四種編碼方案,分析優缺點並給出推薦做法。

為何直接存二進位會失敗?

圖片包含大量特殊位元組(0x00、0x0A、0x5C 等),直接拼接進 SQL 字串會導致語法錯誤:

You have an error in your SQL syntax ... near 'q!...'

四種編碼方案實測比較 (測試檔:17.0 KB JPG)

方案PHP 函式資料庫欄位型別儲存大小讀出還原優缺點總評
Base64base64_encode()TEXT / MEDIUMTEXT~23 KB (+35%)base64_decode()通用性高、可直接用於 Data URI、體積膨脹 33%
Hex (16進制)bin2hex()TEXT / MEDIUMTEXT~34 KB (+100%)MySQL UNHEX() 或 PHP hex2bin()體積倍增、不需解碼即可用 0x... 寫入、除錯易讀
addslashesaddslashes()BLOB / MEDIUMBLOB17 KB (原始)無需解碼理論可行,但實測 Windows/某些 MySQL 版本仍報錯、不穩定
PMA_sqlAddslashes (phpMyAdmin 同款)自訂函式BLOB / MEDIUMBLOB17 KB (原始)無需解碼推薦 體積最小、相容性最好、為 phpMyAdmin 實測方案

PMA_sqlAddslashes 完整實作

function PMA_sqlAddslashes($str, $is_like = false, $crlf = false, $php_code = false) {
    if ($is_like) {
        $str = str_replace('\\', '\\\\', $str);
    } else {
        $str = str_replace('\\', '\\\\', $str); // 實際上同樣處理反斜線
    }
    if ($crlf) {
        $str = str_replace("\n", '\n', $str);
        $str = str_replace("\r", '\r', $str);
        $str = str_replace("\t", '\t', $str);
    }
    if ($php_code) {
        $str = str_replace("'", "\\'", $str);
    } else {
        $str = str_replace("'", "''", $str); // SQL 標準跳脫:單引號變雙單引號
    }
    return $str;
}

此函式模擬 MySQL 字串字面值跳脫規則,將二進位資料安全包裹在 '...' 內。

完整存入 / 讀出範例

上傳表單 (image.html)

<form method="post" action="upimage.php" enctype="multipart/form-data">
    <input type="hidden" name="MAX_FILE_SIZE" value="204800">
    File: <input type="file" name="imgfile">
    <input type="submit" value="OK" name="submitbtn">
</form>

處理上傳並存入資料庫 (upimage.php)

<?php
if (isset($_POST['submitbtn']) && isset($_FILES['imgfile'])) {
    $file = $_FILES['imgfile'];
    if ($file['error'] === UPLOAD_ERR_OK && is_uploaded_file($file['tmp_name'])) {
        $fp = fopen($file['tmp_name'], 'rb');
        $binary = fread($fp, $file['size']);
        fclose($fp);

        // 關鍵:使用 PMA_sqlAddslashes
        $safeBin = PMA_sqlAddslashes($binary);

        $mysqli = new mysqli('localhost', 'root', '123456', 'test');
        $name   = $mysqli->real_escape_string($file['name']);
        $type   = $mysqli->real_escape_string($file['type']);

        $sql = "INSERT INTO `images` (`name`, `type`, `image`) 
                VALUES ('$name', '$type', '$safeBin')";
        if ($mysqli->query($sql)) {
            echo "插入成功!<a href='disimage.php?id={$mysqli->insert_id}'>顯示圖片</a>";
        } else {
            echo "插入失敗:" . $mysqli->error;
        }
        $mysqli->close();
    }
}
?>

讀出並輸出圖片 (disimage.php)

<?php
$id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
if ($id) {
    $mysqli = new mysqli('localhost', 'root', '123456', 'test');
    $stmt = $mysqli->prepare('SELECT `type`, `image` FROM `images` WHERE `id` = ?');
    $stmt->bind_param('i', $id);
    $stmt->execute();
    $stmt->bind_result($type, $image);
    if ($stmt->fetch()) {
        header('Content-Type: ' . $type);
        header('Content-Length: ' . strlen($image));
        echo $image; // 直接輸出二進位
    }
    $stmt->close();
    $mysqli->close();
}
?>

效能與架構建議

  1. 欄位型別選擇: 小圖 BLOB (64KB)、中圖 MEDIUMBLOB (16MB)、大圖 LONGBLOB (4GB)。
  2. 資料庫體積暴增: 圖片存入會使備份檔案巨大、還原緩慢、Replication 延遲。建議獨立建立 images 表,甚至獨立 Schema。
  3. 讀取效能: 每次請求需經 PHP 輸出,無法利用 Web Server 靜態檔案快取、Sendfile、CDN。高流量站請改用檔案系統 + 路徑存資料庫。
  4. SQL 注入防護: 上方範例已使用 prepare() + bind_param(),二進位資料不經 SQL 解析,最安全。

結論

若必須存資料庫,PMA_sqlAddslashes + BLOB/MEDIUMBLOB + Prepared Statement 是體積最小、相容性最好、安全性最高的組合。但請三思:除非有極強理由,否則「檔案系統存圖、資料庫存路徑」仍是架構正解。

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