$_FILES 是 PHP 處理 HTTP POST 檔案上傳的核心超全域陣列。本文詳細說明其結構、各欄位含義、錯誤代碼對照、表單必要屬性、安全處理流程與常見陷阱。
假設表單欄位名為 myFile:
| 鍵名 | 說明 | 範例 |
|---|---|---|
$_FILES['myFile']['name'] | 用戶端原始檔名 | photo.jpg |
$_FILES['myFile']['type'] | MIME 類型(瀏覽器提供,不可信) | image/jpeg |
$_FILES['myFile']['size'] | 檔案大小(位元組) | 2048576 |
$_FILES['myFile']['tmp_name'] | 伺服器暫存檔完整路徑 | /tmp/phpA1B2C3 |
$_FILES['myFile']['error'] | 上傳錯誤代碼(PHP 4.2.0+) | 0 (UPLOAD_ERR_OK) |
| 常數 | 值 | 說明 |
|---|---|---|
UPLOAD_ERR_OK | 0 | 上傳成功,無錯誤 |
UPLOAD_ERR_INI_SIZE | 1 | 超過 php.ini upload_max_filesize 限制 |
UPLOAD_ERR_FORM_SIZE | 2 | 超過表單 MAX_FILE_SIZE 隱藏欄位限制 |
UPLOAD_ERR_PARTIAL | 3 | 檔案僅部分上傳(網路中斷) |
UPLOAD_ERR_NO_FILE | 4 | 沒有檔案被上傳 |
UPLOAD_ERR_NO_TMP_DIR | 6 | 缺少暫存目錄(PHP 5.0.3+) |
UPLOAD_ERR_CANT_WRITE | 7 | 寫入磁碟失敗(權限、空間不足)(PHP 5.1.0+) |
UPLOAD_ERR_EXTENSION | 8 | 被 PHP 擴展中斷(PHP 5.2.0+) |
<form action="upload.php" method="post" enctype="multipart/form-data">
<input type="hidden" name="MAX_FILE_SIZE" value="2097152"> <!-- 2MB,客戶端檢查,可繞過 -->
<input type="file" name="myFile" accept="image/*">
<input type="submit" value="上傳">
</form>
$_FILES 為空。MAX_FILE_SIZE:瀏覽器端提示,不可作為伺服器驗證依據。<?php
// upload.php
$uploadDir = __DIR__ . '/uploads/';
$allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
$maxSize = 2 * 1024 * 1024; // 2MB
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
exit('僅接受 POST 請求');
}
if (!isset($_FILES['myFile']) || $_FILES['myFile']['error'] !== UPLOAD_ERR_OK) {
$errCode = $_FILES['myFile']['error'] ?? UPLOAD_ERR_NO_FILE;
$errors = [
UPLOAD_ERR_INI_SIZE => '檔案過大 (超過 php.ini 限制)',
UPLOAD_ERR_FORM_SIZE => '檔案過大 (超過表單限制)',
UPLOAD_ERR_PARTIAL => '上傳中斷',
UPLOAD_ERR_NO_FILE => '未選擇檔案',
UPLOAD_ERR_NO_TMP_DIR => '伺服器暫存目錄錯誤',
UPLOAD_ERR_CANT_WRITE => '寫入失敗',
UPLOAD_ERR_EXTENSION => '被擴展阻擋',
];
exit('上傳失敗: ' . ($errors[$errCode] ?? '未知錯誤'));
}
$file = $_FILES['myFile'];
// 1. 驗證檔案大小
if ($file['size'] > $maxSize) {
exit('檔案超過 2MB 限制');
}
// 2. 驗證真實 MIME 類型(不可信 $_FILES['type'])
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $file['tmp_name']);
finfo_close($finfo);
if (!in_array($mime, $allowedTypes, true)) {
exit('不允許的檔案類型: ' . $mime);
}
// 3. 驗證圖片真實性(防偽裝腳本)
$imgInfo = getimagesize($file['tmp_name']);
if ($imgInfo === false) {
exit('非有效圖片檔案');
}
// 4. 產生安全檔名(避免覆蓋、路徑遍歷、特殊字元)
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
$allowedExt = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
if (!in_array($ext, $allowedExt)) {
exit('不允許的副檔名');
}
$newName = bin2hex(random_bytes(16)) . '.' . $ext; // 隨機檔名
$destPath = $uploadDir . $newName;
// 5. 移動暫存檔到目標目錄
if (!move_uploaded_file($file['tmp_name'], $destPath)) {
exit('檔案移動失敗,請檢查目錄權限');
}
// 6. 設定檔案權限(僅擁有者讀寫)
chmod($destPath, 0644);
echo "上傳成功!檔案: $newName, 大小: {$file['size']} bytes, 類型: $mime";
?>
$_FILES['type']、$_FILES['name'],必須伺服器端驗證 MIME 與副檔名。move_uploaded_file() 而非 copy() 或 rename(),它會驗證檔案確實為 HTTP 上傳。php_flag engine off 或禁止 .php 執行。../../etc/passwd)。php.ini 設定 upload_max_filesize、post_max_size,Nginx client_max_body_size。<input type="file" name="myFiles[]" multiple>
// PHP 處理
foreach ($_FILES['myFiles']['tmp_name'] as $index => $tmpName) {
if ($_FILES['myFiles']['error'][$index] === UPLOAD_ERR_OK) {
// 處理單檔...
}
}
?>
move_uploaded_file() 移動到永久位置,否則資料遺失。