單一檔案完整範例 upload.php
<?php
// 1. 統一錯誤回應函數
function alert($msg) {
return '<script>alert("' . addslashes($msg) . '");history.back();</script>';
}
// 2. 設定區
$allowType = ['image/jpeg', 'image/gif', 'image/png', 'image/webp'];
$filePath = __DIR__ . '/uploadFileDir/';
$maxSize = 102400; // 100KB
// 3. 接收檔案
$file = $_FILES['file'] ?? null;
if (!$file) exit(alert('未收到檔案'));
// 4. 檢查上傳錯誤碼
if ($file['error'] !== UPLOAD_ERR_OK) {
exit(alert('上傳錯誤,代碼:' . $file['error']));
}
// 5. 檢查檔案大小
if ($file['size'] > $maxSize) {
exit(alert('檔案過大,限制 100KB'));
}
// 6. 驗證真實 MIME 型別(比 $_FILES['type'] 可靠)
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($file['tmp_name']);
if (!in_array($mime, $allowType, true)) {
exit(alert('不支援的檔案類型:' . $mime));
}
// 7. 確保上傳目錄存在
if (!is_dir($filePath) && !mkdir($filePath, 0755, true)) {
exit(alert('建立目錄失敗'));
}
// 8. 產生唯一檔名(避免覆蓋與安全風險)
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
$newName = time() . '_' . bin2hex(random_bytes(8)) . '.' . $ext;
$dest = $filePath . $newName;
// 9. 移動檔案
if (!move_uploaded_file($file['tmp_name'], $dest)) {
exit(alert('儲存失敗,請稍後再試'));
}
// 10. 清理暫存檔(move_uploaded_file 成功後暫存檔會自動刪除,此行可省略)
// unlink($file['tmp_name']);
// 11. 成功回應
echo alert('恭喜,上傳檔案 [' . $newName . '] 成功!');
?>
對應的 HTML 表單
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>檔案上傳</title></head><body>
<form action="upload.php" method="post" enctype="multipart/form-data">
<input type="file" name="file" accept="image/*" required>
<button type="submit">上傳</button>
</form>
</body></html>
關鍵步驟解析
- 錯誤碼檢查:
UPLOAD_ERR_OK (0) 表示成功,其他代碼請查閱官方文件
- MIME 型別驗證:使用
finfo 讀取檔案實際內容,防止偽裝副檔名
- 檔名重組:時間戳 + 隨機字串 + 原副檔名,防止覆蓋與路徑遍歷攻擊
- move_uploaded_file():唯一能確認檔案經由 HTTP POST 上傳的函數
- 目錄權限:
0755 足夠,避免 0777
常見問題排查
| 現象 | 可能原因 | 解決方向 |
| 上傳大檔失敗 | php.ini upload_max_filesize/post_max_size 過小 | 調整 php.ini 或 .user.ini |
| move_uploaded_file 回傳 false | 目標目錄權限不足、磁碟滿、open_basedir 限制 | 檢查目錄擁有者/權限、磁碟空間 |
| 圖片上傳後無法顯示 | 輸出前有 BOM/空白、Header 錯誤、路徑錯誤 | 確保 PHP 檔案無 BOM、檢查輸出路徑 |
| 副檔名大寫導致驗證失敗 | 未轉小寫比對 | strtolower(pathinfo(...)) |
進階擴充建議
- 多檔上傳:
name="files[]" + 迴圈處理 $_FILES['files']
- 分塊上傳/斷點續傳:前端 Plupload/Uppy + 後端合併邏輯
- 圖片壓縮/轉檔:搭配 GD/Imagick 處理
- 雲端儲存:上傳至 S3/OSS 而非本地磁碟
- 病毒掃描:整合 ClamAV 掃描暫存檔