PHP 檔案上傳 (File Upload)
PHP 可以處理使用者透過 HTML 表單上傳的檔案。上傳的檔案會暫存在伺服器上,PHP 腳本可以取得檔案資訊並決定如何處理它(如儲存到指定目錄、驗證類型等)。
上傳表單
要允許使用者上傳檔案,表單必須使用 POST 方法,並設定 enctype="multipart/form-data" 屬性:
<form action="upload.php" method="POST" enctype="multipart/form-data">
<input type="file" name="myfile">
<button type="submit">上傳</button>
</form>
表單必須設定
enctype="multipart/form-data" 才能上傳檔案。處理上傳
上傳的檔案資訊存放在 $_FILES 超全域變數中。每個檔案欄位包含 5 個屬性:name(原始檔名)、type(MIME 類型)、tmp_name(暫存路徑)、error(錯誤碼)、size(檔案大小)。使用 move_uploaded_file() 將檔案從暫存位置移至目標位置:
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_FILES['myfile'])) {
$file = $_FILES['myfile'];
// 檔案資訊
$name = $file['name']; // 原始檔名
$type = $file['type']; // MIME 類型
$tmpName = $file['tmp_name']; // 暫存路徑
$error = $file['error']; // 錯誤碼
$size = $file['size']; // 檔案大小(bytes)
if ($error === UPLOAD_ERR_OK) {
$destination = 'uploads/' . $name;
move_uploaded_file($tmpName, $destination);
echo "上傳成功!";
}
}
}
?>
錯誤處理
$_FILES['欄位名']['error'] 包含上傳狀態碼,UPLOAD_ERR_OK(值為 0)表示成功,其他值表示不同類型的錯誤:
<?php
$errorMessages = [
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 => '上傳被擴展阻止',
];
if ($file['error'] !== UPLOAD_ERR_OK) {
$message = $errorMessages[$file['error']] ?? '未知錯誤';
die("上傳失敗:$message");
}
?>
安全驗證
處理檔案上傳時,安全性非常重要。永遠不要信任使用者提供的資料(包括檔名和 MIME 類型),應該進行多重驗證:
<?php
function uploadFile(array $file, string $uploadDir): array {
$errors = [];
// 1. 檢查錯誤
if ($file['error'] !== UPLOAD_ERR_OK) {
$errors[] = '上傳失敗';
return ['success' => false, 'errors' => $errors];
}
// 2. 檢查檔案大小(5MB)
$maxSize = 5 * 1024 * 1024;
if ($file['size'] > $maxSize) {
$errors[] = '檔案太大(最大 5MB)';
}
// 3. 檢查副檔名
$allowedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'pdf'];
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
if (!in_array($ext, $allowedExtensions)) {
$errors[] = '不允許的檔案類型';
}
// 4. 檢查 MIME 類型
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mimeType = $finfo->file($file['tmp_name']);
$allowedMimes = [
'image/jpeg', 'image/png', 'image/gif', 'application/pdf'
];
if (!in_array($mimeType, $allowedMimes)) {
$errors[] = '檔案類型不符';
}
if (!empty($errors)) {
return ['success' => false, 'errors' => $errors];
}
// 5. 產生安全的檔名
$newName = bin2hex(random_bytes(16)) . '.' . $ext;
$destination = rtrim($uploadDir, '/') . '/' . $newName;
// 6. 移動檔案
if (!move_uploaded_file($file['tmp_name'], $destination)) {
return ['success' => false, 'errors' => ['無法儲存檔案']];
}
return [
'success' => true,
'filename' => $newName,
'path' => $destination
];
}
// 使用
$result = uploadFile($_FILES['myfile'], 'uploads/');
if ($result['success']) {
echo "上傳成功:" . $result['filename'];
} else {
foreach ($result['errors'] as $error) {
echo "錯誤:$error<br>";
}
}
?>
多檔案上傳
<form method="POST" enctype="multipart/form-data">
<input type="file" name="files[]" multiple>
<button type="submit">上傳</button>
</form>
<?php
if (isset($_FILES['files'])) {
$files = $_FILES['files'];
$count = count($files['name']);
for ($i = 0; $i < $count; $i++) {
$file = [
'name' => $files['name'][$i],
'type' => $files['type'][$i],
'tmp_name' => $files['tmp_name'][$i],
'error' => $files['error'][$i],
'size' => $files['size'][$i],
];
// 處理每個檔案
$result = uploadFile($file, 'uploads/');
}
}
?>
php.ini 設定
這些 php.ini 設定會影響檔案上傳行為,需要根據實際需求調整:
; 最大上傳檔案大小
upload_max_filesize = 10M
; POST 資料最大大小
post_max_size = 12M
; 最大檔案上傳數量
max_file_uploads = 20
; 上傳暫存目錄
upload_tmp_dir = /tmp