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

PHP 檔案上傳完整實作指南:從表單到安全存儲的最佳實踐

     編輯:PHP 綜合知識

核心流程圖解

HTML 表單 (multipart/form-data) → $_FILES 超全域陣列 → 伺服器臨時目錄 → 驗證/清洗 → 移動至永久存儲 → 資料庫記錄元資料 → 返回結果

1. 前端表單關鍵點

<form action="upload.php" method="POST" enctype="multipart/form-data">
    <!-- 限制上傳大小(瀏覽器端提示,不可信,伺服器必須再驗) -->
    <input type="hidden" name="MAX_FILE_SIZE" value="2097152"> <!-- 2MB -->
    <input type="file" name="avatar" accept="image/*">
    <button type="submit">上傳</button>
</form>
  • enctype="multipart/form-data":必須,否則 $_FILES 為空。
  • accept:僅客戶端過濾,不可作為安全依據。

2. PHP 端核心變數 $_FILES['avatar'] 結構

鍵名說明
name客戶端原始檔名(含路徑風險,需 basename())
typeMIME 型別(瀏覽器提供,可偽造)
tmp_name伺服器臨時檔案完整路徑(upload_tmp_dir 設定)
size檔案大小(位元組)
error錯誤代碼(0=成功,1=超過 php.ini upload_max_filesize,2=超過 MAX_FILE_SIZE,3=部分上傳,4=未上傳檔案)

3. 安全驗證清單(逐項必做)

  1. 錯誤代碼檢查:if ($_FILES['avatar']['error'] !== UPLOAD_ERR_OK) throw new Exception('上傳失敗');
  2. 檔案大小限制:if ($_FILES['avatar']['size'] > 2 * 1024 * 1024) throw new Exception('檔案過大');
  3. MIME 型別白名單:$allowed = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']; if (!in_array($_FILES['avatar']['type'], $allowed)) throw new Exception('型別不允許');
  4. 真實內容檢測:$finfo = new finfo(FILEINFO_MIME_TYPE); $mime = $finfo->file($_FILES['avatar']['tmp_name']); if (!in_array($mime, $allowed)) throw new Exception('內容偽裝');
  5. 圖片二次渲染(防殘留腳本):$img = imagecreatefromstring(file_get_contents($tmp)); imagejpeg($img, $dest); imagedestroy($img); —— 徹底剝離 EXIF、PHP 代碼注入風險。
  6. 隨機化儲存檔名:$ext = pathinfo($name, PATHINFO_EXTENSION); $newName = bin2hex(random_bytes(16)) . '.' . $ext; 防止覆蓋、路徑遍歷、執行腳本。
  7. 儲存目錄非 Web 可執行:放在 public/ 外,或 Nginx 設定 location ~ \.php$ { deny all; }。

4. 移動檔案與資料庫記錄

$destDir = __DIR__ . '/../storage/uploads/' . date('Y/m/d');
if (!is_dir($destDir)) mkdir($destDir, 0755, true);
$destPath = $destDir . '/' . $newName;

if (!move_uploaded_file($_FILES['avatar']['tmp_name'], $destPath)) {
    throw new Exception('儲存失敗');
}

// 寫入資料庫
$stmt = $pdo->prepare("INSERT INTO uploads (original_name, stored_path, mime, size, user_id, created_at) VALUES (?, ?, ?, ?, ?, NOW())");
$stmt->execute([$originalName, $destPath, $mime, $size, $userId]);

5. 常見坑與避雷指南

坑點後果修正
直接用 $_FILES['avatar']['name'] 當檔名覆蓋、路徑遍歷、腳本執行隨機化 + 白名單副檔名
信任 type 忽略 finfo上傳 .php.jpg 繞過驗證二進制內容檢測 + 圖片重渲染
未設 upload_max_filesize / post_max_size大檔案靜默失敗php.ini / .user.ini / Nginx client_max_body_size 三層同步
臨時檔案未清理磁碟佔滿move_uploaded_file 成功自動刪除;失敗需手動 unlink

6. 進階:大檔案分片上傳(斷點續傳)

  • 前端:spark-md5 計算檔案雜湊 → 切片 → 並發上傳切片 → 合併請求。
  • 後端:臨時目錄收集切片 → 驗證雜湊 → 合併 → 入庫。
  • 工具:WebUploader、Uppy、Resumable.js。

7. 雲端儲存整合(S3 / OSS / COS)

// 以 AWS S3 為例
$s3 = new Aws\S3\S3Client([...]);
$s3->putObject([
    'Bucket' => 'my-bucket',
    'Key'    => "uploads/{$newName}",
    'SourceFile' => $destPath,
    'ACL'    => 'private',
]);
// 刪除本地暫存檔
unlink($destPath);

原文代碼已重構為現代安全標準,補充 finfo 驗證、圖片重渲染、隨機命名、雲端儲存等生產級實踐。

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