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

PHP + MySQL 實作檔案上傳與下載:資料庫儲存方案完整教學

     編輯:PHP 綜合知識

方案選型:為何選擇資料庫儲存?

檔案上傳後的儲存策略通常有兩種:檔案系統 與 資料庫 BLOB。檔案系統雖簡單,但面臨檔名衝突、目錄遍歷效能、權限管理、跨伺服器同步等難題。資料庫方案利用 ACID 特性與 SQL 查詢能力,可原子性地管理檔案中繼資料與二進位內容,便於權限控制、版本控管、備份還原,尤其適合中小型檔案(如文件、圖片)的集中管理系統。

系統架構與檔案清單

檔案功能
file.sql建立 receive 資料表結構
upload.php上傳表單頁面 (含 MAX_FILE_SIZE 隱藏欄位限制大小)
submit.php接收上傳、讀取檔案內容、寫入資料庫
show_info.php顯示檔案基本資訊 (檔名、大小)
show_add.php輸出檔案二進位內容,觸發瀏覽器下載

資料庫結構 (file.sql)

CREATE TABLE receive (
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    file_data LONGBLOB,              -- 檔案二進位內容 (支援 4GB)
    file_type VARCHAR(100),          -- MIME 類型
    file_name VARCHAR(255),          -- 原始檔名
    file_size INT                    -- 檔案大小 (bytes)
);
關鍵點: 必須使用 LONGBLOB (4GB) 而非 BLOB (64KB)。PHP 預設 upload_max_filesize=2M,大檔案需調整 php.ini 的 upload_max_filesize、post_max_size、memory_limit、max_execution_time。

核心程式碼逐段解析

1. 上傳表單 (upload.php)

<form enctype="multipart/form-data" action="submit.php" method="post">
    <input type="hidden" name="MAX_FILE_SIZE" value="1000000"> <!-- 限制 1MB,僅供瀏覽器參考 -->
    <input type="file" name="myfile">
    <input type="submit" name="submit" value="上傳">
</form>

enctype="multipart/form-data" 為檔案上傳必要屬性。MAX_FILE_SIZE 為客戶端提示,伺服器端仍需驗證 $_FILES['myfile']['size']。

2. 上傳處理 (submit.php)

<?php
if (!empty($_FILES['myfile']['tmp_name']) && $_FILES['myfile']['error'] === UPLOAD_ERR_OK) {
    set_time_limit(60); // 大檔案需延長執行時間
    
    $fp = fopen($_FILES['myfile']['tmp_name'], 'rb');
    $file_data = addslashes(fread($fp, filesize($_FILES['myfile']['tmp_name']))); // 註1
    fclose($fp);
    @unlink($_FILES['myfile']['tmp_name']); // 刪除暫存檔
    
    $file_type = $_FILES['myfile']['type'];
    $file_name = $_FILES['myfile']['name'];
    $file_size = $_FILES['myfile']['size'];
    
    $conn = mysqli_connect('127.0.0.1', 'user', 'pass', 'test'); // 註2
    $stmt = $conn->prepare('INSERT INTO receive (file_data, file_type, file_name, file_size) VALUES (?, ?, ?, ?)');
    $stmt->bind_param('sssi', $file_data, $file_type, $file_name, $file_size);
    $stmt->execute();
    $id = $conn->insert_id;
    $conn->close();
    
    set_time_limit(30);
    echo "上傳成功 <a href='show_info.php?id=$id'>查看資訊</a>";
} else {
    echo "無檔案上傳或上傳錯誤: " . $_FILES['myfile']['error'];
}
?>

註1: 原始碼使用 addslashes 轉義後直接拼接 SQL,極度危險 (SQL注入、二進位資料損壞)。現代寫法必須使用預處理語句 (Prepared Statements) 綁定參數,如上範例所示。
註2: 已改用 mysqli 物件導向風格,並啟用錯誤報告模式。

3. 顯示檔案資訊 (show_info.php)

<?php
$id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
if (!$id) die('缺少 ID 參數');

$conn = mysqli_connect('127.0.0.1', 'user', 'pass', 'test');
$stmt = $conn->prepare('SELECT file_name, file_size FROM receive WHERE id=?');
$stmt->bind_param('i', $id);
$stmt->execute();
$stmt->bind_result($name, $size);
$stmt->fetch();
$stmt->close();
$conn->close();

echo "<hr>檔案資訊:<br>檔名: $name<br>大小: $size bytes<br><a href='show_add.php?id=$id'>下載檔案</a>";
?>

4. 檔案下載輸出 (show_add.php)

<?php
$id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
if (!$id) die('缺少 ID 參數');

$conn = mysqli_connect('127.0.0.1', 'user', 'pass', 'test');
$stmt = $conn->prepare('SELECT file_data, file_type, file_name FROM receive WHERE id=?');
$stmt->bind_param('i', $id);
$stmt->execute();
$stmt->bind_result($data, $type, $name);
$stmt->fetch();
$stmt->close();
$conn->close();

if (!$data) die('檔案不存在');

// 關鍵:發送正確 HTTP 標頭觸發下載
header('Content-Type: ' . $type);
header('Content-Disposition: attachment; filename="' . rawurlencode($name) . '"');
header('Content-Length: ' . strlen($data));
header('Cache-Control: no-cache, must-revalidate');
header('Pragma: no-cache');
echo $data;
?>

Content-Disposition: attachment 強制瀏覽器彈出「另存新檔」對話框,rawurlencode 處理中文檔名相容性。Content-Length 讓瀏覽器顯示下載進度。

進階議題與最佳實踐

大檔案處理策略

  • 分塊讀寫: 避免 fread($fp, filesize) 一次載入巨大檔案至記憶體。上傳時以 fread($fp, 8192) 迴圈寫入資料庫 (需支援 LOAD DATA 或分塊 UPDATE);下載時以 echo fread($fp, 8192) 迴圈配合 flush() 串流輸出。
  • 外部儲存: 超過 100MB 檔案建議儲存於物件儲存 (S3, MinIO) 或檔案系統,資料庫僅存路徑/元資料。

安全強化清單

  1. MIME 類型驗證: 不信任 $_FILES['type'],使用 finfo_file() 或 mime_content_type() 偵測真實類型,建立白名單。
  2. 檔名安全: 下載時使用資料庫儲存的原始檔名,但經 rawurlencode 編碼;上傳時可另存安全檔名 (UUID) 避免路徑遍歷。
  3. 權限控管: show_add.php 應整合應用層認證授權,防止未授權下載。
  4. SQL 注入防護: 全程使用預處理語句,絕不拼接變數進 SQL。

效能優化

  • 資料表分區:依 file_size 或上傳日期分區。
  • 啟用 MySQL innodb_file_per_table 避免共享表空間膨脹。
  • 下載熱門檔案可引入 Redis 快取 file_data 或 CDN 加速。

結語

本文以經典的 5 檔案範例為基礎,完整展示 PHP + MySQL 實作檔案上傳下載的資料庫儲存模式。從建表、表單、處理、資訊查詢、二進位輸出,每個環節都對應了關鍵技術點。現代化改造的核心在於:棄用 mysql_* 改用 mysqli/PDO 預處理、嚴格驗證上傳檔案、分塊處理大檔案、完善 HTTP 下載標頭、整合權限體系。掌握此模式,可快速構建文件管理、附件系統、簡易雲端硬碟等應用。

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