核心流程
- HTML 表單設置
enctype="multipart/form-data"、method="post"。
- PHP 通過
$_FILES 超全局變量獲取上傳文件元數據。
- 驗證:
is_uploaded_file() 確認合法上傳、MIME 類型白名單、文件大小限制、錯誤碼檢查。
- 安全存儲:
move_uploaded_file() 將臨時文件移動至不可直接訪問的目錄,重命名防覆蓋。
- 回饋結果:JSON 或頁面提示成功/失敗原因。
完整代碼示例
<?php
// upload.php
$allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
$maxSize = 5 * 1024 * 1024; // 5MB
$uploadDir = __DIR__ . '/uploads/';
if (!is_dir($uploadDir)) mkdir($uploadDir, 0755, true);
$response = ['success' => false, 'message' => '', 'file' => null];
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['uploadfile'])) {
$file = $_FILES['uploadfile'];
// 1. 檢查上傳錯誤
if ($file['error'] !== UPLOAD_ERR_OK) {
$response['message'] = '上傳錯誤代碼: ' . $file['error'];
}
// 2. 驗證 MIME 類型 (需配合 finfo 擴展防偽裝)
elseif (!in_array($file['type'], $allowedTypes)) {
$response['message'] = '不允許的文件類型: ' . $file['type'];
}
// 3. 驗證大小
elseif ($file['size'] > $maxSize) {
$response['message'] = '文件過大,限制 ' . ($maxSize/1024/1024) . 'MB';
}
// 4. 合法性檢查
elseif (!is_uploaded_file($file['tmp_name'])) {
$response['message'] = '非法上傳文件';
}
else {
// 5. 生成唯一文件名 防覆蓋
$ext = pathinfo($file['name'], PATHINFO_EXTENSION);
$newName = bin2hex(random_bytes(16)) . '.' . strtolower($ext);
$destPath = $uploadDir . $newName;
// 6. 移動文件
if (move_uploaded_file($file['tmp_name'], $destPath)) {
$response['success'] = true;
$response['message'] = '上傳成功';
$response['file'] = ['name' => $file['name'], 'path' => $newName, 'size' => $file['size']];
} else {
$response['message'] = '文件移動失敗,請檢查目錄權限';
}
}
}
// 輸出 JSON 供前端 AJAX 使用
header('Content-Type: application/json');
echo json_encode($response);
?>
<!-- HTML 表單 -->
<form action="upload.php" method="post" enctype="multipart/form-data" id="uploadForm">
<input type="file" name="uploadfile" accept="image/*" required>
<button type="submit">上傳</button>
</form>
<script>
// 可選:AJAX 無刷新上傳
document.getElementById('uploadForm').addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const res = await fetch('upload.php', {method: 'POST', body: formData}).then(r => r.json());
alert(res.message);
if (res.success) console.log('文件路徑:', res.file.path);
});
</script>
關鍵安全點總結
- 類型驗證:不要只信任
$_FILES['type'],使用 finfo_file() 檢測真實 MIME。
- 存儲路徑:上傳目錄置於 Web 根目錄外,或配置 Nginx/Apache 禁止腳本執行 (
location ~ \.php$ { deny all; })。
- 文件名:隨機重命名 + 保留副檔名,避免中文文件名編碼問題與覆蓋風險。
- 大小限制:同時設置 PHP
upload_max_filesize、post_max_size 及前端 MAX_FILE_SIZE 隱藏域(僅作用戶提示)。
- 圖片二次渲染:對圖片使用 GD/Imagick 重新壓縮儲存,剝離潛在惡意代碼。
詳細說明:https://php.662p.com/thread-570-1-1.html