คู่มือฉบับสมบูรณ์สำหรับการอัปโหลดหลายไฟล์ (Multiple File Upload) ใน PHP พร้อม Validation, Security และ Error Handling
HTML Form (สำคัญ: name ต้องเป็น Array และมี multiple attribute)
<form action="upload.php" method="POST" enctype="multipart/form-data">
<input type="file" name="upload[]" id="upload" multiple accept="image/*,.pdf">
<button type="submit">อัปโหลด</button>
</form>
จุดสำคัญ:
name="upload[]"- ต้องมี [] เพื่อรับเป็น Arraymultiple- อนุญาตเลือกหลายไฟล์enctype="multipart/form-data"- จำเป็นสำหรับ File Uploadaccept- จำกัดประเภทไฟล์ฝั่ง Client (แต่ต้อง Validate ฝั่ง Server อีกครั้ง)
PHP: การจัดการอัปโหลดแบบสมบูรณ์
$maxSize) {
$errors[] = "File size exceeds " . ($maxSize / 1024 / 1024) . " MB";
}
// ตรวจสอบ MIME Type จริง (ไม่ไว้ใจ $_FILES['type'])
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $file['tmp_name']);
finfo_close($finfo);
if (!in_array($mimeType, $allowedTypes)) {
$errors[] = "File type not allowed: $mimeType";
}
// ตรวจสอบว่าเป็นรูปภาพจริง (สำหรับ Image)
if (str_starts_with($mimeType, 'image/')) {
$imgInfo = getimagesize($file['tmp_name']);
if (!$imgInfo) {
$errors[] = "Invalid image file";
}
}
return $errors;
}
// === Main Upload Logic ===
$results = ['success' => [], 'errors' => []];
if (!empty($_FILES['upload']['name'][0])) {
$fileCount = count($_FILES['upload']['name']);
// จำกัดจำนวนไฟล์
if ($fileCount > $maxFiles) {
$results['errors'][] = "Too many files. Maximum $maxFiles files allowed.";
} else {
for ($i = 0; $i < $fileCount; $i++) {
// ข้ามไฟล์ว่าง (user เลือกไฟล์แล้วลบออก)
if (empty($_FILES['upload']['name'][$i]) || $_FILES['upload']['error'][$i] === UPLOAD_ERR_NO_FILE) {
continue;
}
$file = [
'name' => $_FILES['upload']['name'][$i],
'type' => $_FILES['upload']['type'][$i],
'tmp_name' => $_FILES['upload']['tmp_name'][$i],
'error' => $_FILES['upload']['error'][$i],
'size' => $_FILES['upload']['size'][$i],
];
$errors = validateFile($file, $allowedTypes, $maxFileSize);
if (empty($errors)) {
$newName = generateSafeFilename($file['name']);
$destPath = $uploadDir . $newName;
if (move_uploaded_file($file['tmp_name'], $destPath)) {
$results['success'][] = [
'original_name' => $file['name'],
'saved_name' => $newName,
'size' => $file['size'],
'path' => $destPath
];
} else {
$results['errors'][] = "Failed to move: {$file['name']}";
}
} else {
$results['errors'][] = "{$file['name']}: " . implode(', ', $errors);
}
}
}
}
// === Response ===
header('Content-Type: application/json');
echo json_encode($results, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
?>
JavaScript (AJAX) สำหรับ UX ที่ดีกว่า
// ใช้ FormData สำหรับ AJAX File Upload
document.getElementById('uploadForm').addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const response = await fetch('upload.php', {
method: 'POST',
body: formData
});
const result = await response.json();
if (result.success.length) {
console.log('Uploaded:', result.success);
// แสดง Preview, Progress, etc.
}
if (result.errors.length) {
console.error('Errors:', result.errors);
}
});
Security Checklist
- ✅ Validate MIME Type ด้วย
finfo(ไม่ไว้ใจ $_FILES['type']) - ✅ ตรวจสอบขนาดไฟล์
- ✅ เปลี่ยนชื่อไฟล์ (ป้องกัน Overwrite, Path Traversal)
- ✅ จำกัดประเภทและขนาดไฟล์
- ✅ เก็บไฟล์นอก Document Root (ถ้าเป็นไปได้)
- ✅ ตั้ง Permission โฟลเดอร์ 0755, ไฟล์ 0644
- ✅ Scan ไวรัส (ClamAV) สำหรับ Production จริง