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

PHP 多檔案上傳完整實作:陣列名稱技巧、迴圈處理、安全過濾

     編輯:PHP 相關知識

前言

HTML 表單上傳多檔案的關鍵在於 input name="file[]" 的陣列語法,PHP 端會收到結構化的 $_FILES['file'] 陣列。本文提供從前端表單到後端處理的完整流程,包含 檔案類型驗證、大小限制、隨機重命名防衝突、錯誤碼處理、資料夾權限檢查。

前端表單(upload.html)

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>多檔案上傳</title>
</head>
<body>
    <form action="upload.php" method="post" enctype="multipart/form-data">
        <!-- 關鍵:name="file[]" 讓瀏覽器送出陣列 -->
        <input type="file" name="file[]" multiple><br>
        <input type="file" name="file[]" multiple><br>
        <input type="file" name="file[]" multiple><br>
        <button type="submit">上傳</button>
    </form>
</body>
</html>

技巧:單一 <input name="file[]" multiple> 即可選取多檔,多個 input 只是 UI 習慣。

後端處理(upload.php)

<?php
header("Content-Type: text/html; charset=utf-8");

// 1. 設定上傳限制
const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'application/pdf'];
const UPLOAD_DIR = __DIR__ . '/uploads/files';

// 2. 確保上傳目錄存在且可寫
if (!is_dir(UPLOAD_DIR)) {
    if (!mkdir(UPLOAD_DIR, 0755, true)) {
        die('無法建立上傳目錄');
    }
}
if (!is_writable(UPLOAD_DIR)) {
    die('上傳目錄不可寫入');
}

// 3. 檢查是否有檔案上傳
if (!isset($_FILES['file']) || empty($_FILES['file']['name'][0])) {
    die('請選擇檔案');
}

$success = 0;
$errors = [];

// 4. 迴圈處理每個檔案
$count = count($_FILES['file']['name']);
for ($i = 0; $i < $count; $i++) {
    // 4.1 取得單一檔案資訊
    $name     = $_FILES['file']['name'][$i];
    $type     = $_FILES['file']['type'][$i];
    $tmpName  = $_FILES['file']['tmp_name'][$i];
    $error    = $_FILES['file']['error'][$i];
    $size     = $_FILES['file']['size'][$i];

    // 4.2 錯誤碼檢查
    if ($error !== UPLOAD_ERR_OK) {
        $errors[] = "{$name}: " . uploadErrorMessage($error);
        continue;
    }

    // 4.3 檔案大小檢查
    if ($size > MAX_FILE_SIZE) {
        $errors[] = "{$name}: 檔案過大(限制 " . (MAX_FILE_SIZE/1024/1024) . "MB)";
        continue;
    }

    // 4.4 MIME 類型檢查(注意:瀏覽器提供,不可全信,建議搭配 finfo)
    if (!in_array($type, ALLOWED_TYPES, true)) {
        // 二次驗證:使用 finfo 檢測真實類型
        $finfo = new finfo(FILEINFO_MIME_TYPE);
        $realType = $finfo->file($tmpName);
        if (!in_array($realType, ALLOWED_TYPES, true)) {
            $errors[] = "{$name}: 禁止的檔案類型(偵測為 {$realType})";
            continue;
        }
    }

    // 4.5 產生唯一檔名:時間戳 + 隨機數 + 原副檔名
    $ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
    $newName = sprintf('%s_%s.%s', time(), mt_rand(10000, 99999), $ext);
    $destPath = UPLOAD_DIR . DIRECTORY_SEPARATOR . $newName;

    // 4.6 移動檔案
    if (move_uploaded_file($tmpName, $destPath)) {
        $success++;
        // 可選:記錄資料庫、產生縮圖、掃毒...
    } else {
        $errors[] = "{$name}: 移動失敗(權限或磁碟空間)";
    }
}

// 5. 輸出結果
echo "

上傳完成:成功 {$success} 筆,失敗 " . count($errors) . " 筆

"; if ($errors) { echo "
    "; foreach ($errors as $e) echo "
  • " . htmlspecialchars($e) . "
  • "; echo "
"; } echo "返回繼續上傳"; // 錯誤碼對照表 function uploadErrorMessage(int $code): string { return match ($code) { 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 => '寫入磁碟失敗', UPLOAD_ERR_EXTENSION => '被 PHP 擴充功能阻止', default => '未知錯誤', }; }

關鍵陣列結構對照表

鍵名說明範例值
$_FILES['file']['name'][$i]原始檔名photo.jpg
$_FILES['file']['type'][$i]瀏覽器回報 MIMEimage/jpeg
$_FILES['file']['tmp_name'][$i]暫存檔完整路徑/tmp/phpA1B2C3
$_FILES['file']['error'][$i]錯誤碼(0=成功)0
$_FILES['file']['size'][$i]檔案大小(位元組)102400

安全性加強清單

  1. 副檔名白名單:除了 MIME,亦檢查 pathinfo($name, PATHINFO_EXTENSION) ∈ ['jpg','png','gif','pdf']。
  2. 檔案內容掃描:上傳圖片可用 getimagesize() 驗證真為圖片;PDF 可用 finfo 確認 header %PDF。
  3. 儲存路徑非 Web 可執行目錄:uploads/ 應置於 public/ 外層,或 Nginx 設定 location ~ \.php$ { deny all; }。
  4. 檔名隨機化:避免使用者上傳 shell.php.jpg 並嘗試解析執行。
  5. 限制總上傳大小:php.ini post_max_size、upload_max_filesize、表單隱藏欄位 MAX_FILE_SIZE 三重保護。

結語

多檔案上傳的核心在於理解 $_FILES 的陣列結構,並對每個檔案獨立執行「驗證 → 重命名 → 移動 → 記錄」流程。建議將上述邏輯封裝為 FileUploader 類別,支援 驗證器鏈、儲存策略(本地/S3/OSS)、事件鉤子(上傳後縮圖/掃毒/入庫),即可滿足企業級檔案管理系統需求。

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