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

PHP 檔案上傳完整處理流程與安全最佳實踐

     編輯:PHP 相關知識

上傳流程圖解

  1. 用戶提交 enctype="multipart/form-data" 表單
  2. PHP 將檔案暫存至 upload_tmp_dir (預設系統暫存目錄)
  3. 腳本驗證 $_FILES 資訊(型別、大小、錯誤碼)
  4. 呼叫 move_uploaded_file() 移至永久儲存目錄
  5. 記錄資料庫、回應前端

HTML 表單必要條件

<form action="upload.php" method="post" enctype="multipart/form-data">
  <!-- 隱藏域限制最大檔案大小 (bytes),僅供瀏覽器參考,後端必須再驗證 -->
  <input type="hidden" name="MAX_FILE_SIZE" value="2097152"> <!-- 2MB -->
  <input type="file" name="avatar" accept="image/*">
  <button type="submit">上傳</button>
</form>

$_FILES 陣列結構詳解

鍵名說明範例
name用戶端原始檔名photo.jpg
type瀏覽器回報 MIME (不可信)image/jpeg
tmp_name伺服器暫存路徑/tmp/phpA1B2C3
size檔案大小 (bytes)102400
error錯誤代碼 (0=成功)0

核心驗證與處理程式碼

<?php
// upload.php
const MAX_SIZE = 2 * 1024 * 1024; // 2MB
const ALLOWED_MIME = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
const ALLOWED_EXT  = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
const UPLOAD_DIR   = __DIR__ . '/uploads/avatars/';

$field = 'avatar';
$response = ['success' => false, 'message' => '', 'path' => ''];

// 1. 檢查是否有檔案上傳
if (!isset($_FILES[$field]) || $_FILES[$field]['error'] !== UPLOAD_ERR_OK) {
    $errors = [
        UPLOAD_ERR_INI_SIZE   => '超過 php.ini upload_max_filesize',
        UPLOAD_ERR_FORM_SIZE  => '超過表單 MAX_FILE_SIZE',
        UPLOAD_ERR_PARTIAL    => '檔案上傳不完整',
        UPLOAD_ERR_NO_FILE    => '未選擇檔案',
        UPLOAD_ERR_NO_TMP_DIR => '找不到暫存目錄',
        UPLOAD_ERR_CANT_WRITE => '寫入暫存失敗',
    ];
    $response['message'] = $errors[$_FILES[$field]['error'] ?? '未知錯誤'];
    json_exit($response);
}

// 2. 基本屬性驗證
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime  = $finfo->file($_FILES[$field]['tmp_name']);
$ext   = strtolower(pathinfo($_FILES[$field]['name'], PATHINFO_EXTENSION));

if (!in_array($mime, ALLOWED_MIME, true)) {
    $response['message'] = '檔案類型不允許 (MIME)';
    json_exit($response);
}
if (!in_array($ext, ALLOWED_EXT, true)) {
    $response['message'] = '副檔名不允許';
    json_exit($response);
}
if ($_FILES[$field]['size'] > MAX_SIZE) {
    $response['message'] = '檔案過大';
    json_exit($response);
}

// 3. 圖片二次驗證 (防偽裝腳本)
$imgInfo = getimagesize($_FILES[$field]['tmp_name']);
if (!$imgInfo || !in_array($imgInfo[2], [IMAGETYPE_JPEG, IMAGETYPE_PNG, IMAGETYPE_GIF, IMAGETYPE_WEBP])) {
    $response['message'] = '非有效圖片檔案';
    json_exit($response);
}

// 4. 生成安全檔名 (避免覆蓋、路徑遍歷)
$safeName = bin2hex(random_bytes(16)) . '.' . $ext;
$destPath = UPLOAD_DIR . $safeName;

if (!is_dir(UPLOAD_DIR)) {
    mkdir(UPLOAD_DIR, 0755, true);
}

// 5. 移動檔案
if (!move_uploaded_file($_FILES[$field]['tmp_name'], $destPath)) {
    $response['message'] = '檔案移動失敗,請檢查權限';
    json_exit($response);
}

// 6. 可選:壓縮/裁剪/水印處理...
// require 'ThumbnailImage.php'; // 你的縮圖類

// 7. 成功回應
$response['success'] = true;
$response['message'] = '上傳成功';
$response['path']    = '/uploads/avatars/' . $safeName; // 前端可直接用此 URL
json_exit($response);

function json_exit(array $data): never {
    header('Content-Type: application/json; charset=utf-8');
    echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
}
?>

php.ini 關鍵設定

指令建議值說明
file_uploadsOn啟用 HTTP 上傳
upload_max_filesize10M單一檔案上限
post_max_size12MPOST 總資料上限,必須 > upload_max_filesize
memory_limit128M腳本記憶體上限,必須 > post_max_size
max_execution_time30腳本執行秒數 (大檔上傳需調大)
max_input_time60接收 POST 資料秒數
upload_tmp_dir/var/tmp/php_upload指定專用暫存目錄,確保權限正確

安全防護清單

  • ✅ 使用 finfo + getimagesize() 雙重驗證檔案真實類型
  • ✅ 檔名重命名為隨機雜湊,防止覆蓋與路徑注入
  • ✅ 儲存目錄設定 php_flag engine off (Apache) 或 Nginx location ~ \.php$ { deny all; } 禁止腳本執行
  • ✅ 限制上傳大小、副檔名、MIME 白名單
  • ✅ 資料庫僅存相對路徑,前端輸出時拼接完整 URL
  • ✅ 定期掃描上傳目錄清理孤立檔案

大檔/分塊上傳進階

超過 100MB 建議採用前端分片(如 Uppy、Dropzone、WebUploader)+ 後端合併策略,避免單次請求超時與記憶體壓力。

總結

檔案上傳看似簡單,實則涉及驗證、儲存、權限、效能多個面向。採用「白名單驗證 + 隨機命名 + 執行權限隔離 + 結構化錯誤回應」的標準化流程,能有效阻擋 99% 的上傳型攻擊並提升維護性。

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