PHP多文件上传定制咨询:五文件同传、独立进度条等功能实现
Hey there! Let's tackle your PHP file upload needs step by step. I'll break this down into modular, reusable components so you don't have to copy-paste code everywhere, plus add all the features you asked for: 5 independent uploads with individual progress bars, a single trigger button, MIME type restrictions, and custom file renaming.
First, let's build the UI with 5 file inputs and their corresponding progress bars, plus a single upload button. We'll use vanilla JS to handle the uploads and progress tracking—no fancy frameworks needed.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Multi-File Upload with Progress</title> <style> .upload-group { margin: 15px 0; } .progress-bar { width: 100%; height: 20px; border: 1px solid #ccc; margin-top: 5px; } .progress-fill { height: 100%; width: 0%; background-color: #4CAF50; transition: width 0.3s ease; } .upload-status { margin-top: 5px; font-size: 14px; } </style> </head> <body> <div class="upload-group"> <label>File 1:</label> <input type="file" class="upload-input" data-index="0"> <div class="progress-bar"><div class="progress-fill"></div></div> <div class="upload-status" data-index="0"></div> </div> <div class="upload-group"> <label>File 2:</label> <input type="file" class="upload-input" data-index="1"> <div class="progress-bar"><div class="progress-fill"></div></div> <div class="upload-status" data-index="1"></div> </div> <div class="upload-group"> <label>File 3:</label> <input type="file" class="upload-input" data-index="2"> <div class="progress-bar"><div class="progress-fill"></div></div> <div class="upload-status" data-index="2"></div> </div> <div class="upload-group"> <label>File 4:</label> <input type="file" class="upload-input" data-index="3"> <div class="progress-bar"><div class="progress-fill"></div></div> <div class="upload-status" data-index="3"></div> </div> <div class="upload-group"> <label>File 5:</label> <input type="file" class="upload-input" data-index="4"> <div class="progress-bar"><div class="progress-fill"></div></div> <div class="upload-status" data-index="4"></div> </div> <button id="upload-all-btn">Upload All Files</button> <script> // Configuration: Define allowed MIME types here const ALLOWED_MIME_TYPES = [ 'image/jpeg', 'image/png', 'application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' ]; // Get elements const uploadBtn = document.getElementById('upload-all-btn'); const uploadInputs = document.querySelectorAll('.upload-input'); // Validate MIME type (frontend check—backend will also verify!) function validateMimeType(file) { return ALLOWED_MIME_TYPES.includes(file.type); } // Update progress bar for a specific file function updateProgress(index, percentage) { const progressFill = document.querySelector(`.progress-fill[data-index="${index}"]`); const statusText = document.querySelector(`.upload-status[data-index="${index}"]`); progressFill.style.width = `${percentage}%`; statusText.textContent = `Uploading: ${percentage}%`; } // Handle successful upload function handleUploadSuccess(index, response) { const statusText = document.querySelector(`.upload-status[data-index="${index}"]`); if (response.success) { statusText.textContent = `Success! Saved as: ${response.newFilename}`; statusText.style.color = '#4CAF50'; } else { statusText.textContent = `Error: ${response.error}`; statusText.style.color = '#f44336'; } } // Upload a single file function uploadFile(file, index) { // Skip if no file selected if (!file) return; // Frontend MIME check if (!validateMimeType(file)) { const statusText = document.querySelector(`.upload-status[data-index="${index}"]`); statusText.textContent = `Error: Invalid file type. Allowed types: ${ALLOWED_MIME_TYPES.join(', ')}`; statusText.style.color = '#f44336'; return; } // Create FormData and add the file const formData = new FormData(); formData.append('uploaded_file', file); formData.append('file_index', index); // Send index to backend for renaming // Create XHR request const xhr = new XMLHttpRequest(); xhr.open('POST', 'upload_handler.php', true); // Track upload progress xhr.upload.addEventListener('progress', (e) => { if (e.lengthComputable) { const percentage = Math.round((e.loaded / e.total) * 100); updateProgress(index, percentage); } }); // Handle response xhr.addEventListener('load', () => { if (xhr.status === 200) { const response = JSON.parse(xhr.responseText); handleUploadSuccess(index, response); } else { const statusText = document.querySelector(`.upload-status[data-index="${index}"]`); statusText.textContent = 'Upload failed: Server error'; statusText.style.color = '#f44336'; } }); // Send the request xhr.send(formData); } // Trigger all uploads when button is clicked uploadBtn.addEventListener('click', () => { uploadInputs.forEach((input, index) => { const file = input.files[0]; uploadFile(file, index); }); }); </script> </body> </html>
Now let's build the backend handler (upload_handler.php) with modular functions for validation, renaming, and saving. This way, you can reuse these functions elsewhere if needed.
<?php // Configuration define('UPLOAD_DIR', './uploads/'); // Make sure this folder exists and is writable! define('ALLOWED_MIME_TYPES', [ 'image/jpeg', 'image/png', 'application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' ]); // Create upload directory if it doesn't exist if (!file_exists(UPLOAD_DIR)) { mkdir(UPLOAD_DIR, 0755, true); } // Sanitize filename to prevent security issues function sanitizeFilename($filename) { $filename = pathinfo($filename, PATHINFO_FILENAME); return preg_replace('/[^a-zA-Z0-9-_]/', '', $filename); } // Validate file MIME type (backend check—critical for security!) function validateFileMime($file) { // Use finfo for more accurate MIME detection (better than $_FILES['type']) $finfo = new finfo(FILEINFO_MIME_TYPE); $detectedMime = $finfo->file($file['tmp_name']); return in_array($detectedMime, ALLOWED_MIME_TYPES); } // Rename file to your custom format function getCustomFilename($index, $originalFile) { // Example: "custom_upload_0.jpg", "custom_upload_1.pdf", etc. // Adjust this format to your needs! $extension = pathinfo($originalFile['name'], PATHINFO_EXTENSION); return "custom_upload_{$index}.{$extension}"; } // Save uploaded file to disk function saveUploadedFile($file, $newFilename) { $destination = UPLOAD_DIR . $newFilename; return move_uploaded_file($file['tmp_name'], $destination); } // Main handler $response = ['success' => false, 'error' => '', 'newFilename' => '']; if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['uploaded_file']) && isset($_POST['file_index'])) { $uploadedFile = $_FILES['uploaded_file']; $fileIndex = intval($_POST['file_index']); // Check for upload errors if ($uploadedFile['error'] !== UPLOAD_ERR_OK) { $response['error'] = "Upload error: Code {$uploadedFile['error']}"; echo json_encode($response); exit; } // Validate MIME type if (!validateFileMime($uploadedFile)) { $response['error'] = "Invalid file type. Allowed types: " . implode(', ', ALLOWED_MIME_TYPES); echo json_encode($response); exit; } // Generate custom filename $newFilename = getCustomFilename($fileIndex, $uploadedFile); // Save the file if (saveUploadedFile($uploadedFile, $newFilename)) { $response['success'] = true; $response['newFilename'] = $newFilename; } else { $response['error'] = "Failed to save file to server"; } } else { $response['error'] = "Invalid request"; } echo json_encode($response); ?>
- Reusable Functions: We split the backend into small, single-purpose functions (
sanitizeFilename,validateFileMime, etc.) so you can easily modify or reuse them without rewriting code. - Centralized Configuration: All settings (allowed MIME types, upload directory) are defined at the top, making it easy to update later.
- Frontend/Backend Validation: We check MIME types both in the browser and on the server—never trust frontend validation alone!
- Create an
uploadsfolder in the same directory as your HTML and PHP files, and set permissions to 0755 (or 0777 if needed, but 0755 is more secure). - Test with different file types to make sure invalid ones are rejected.
- Check the
uploadsfolder to confirm files are renamed correctly (e.g.,custom_upload_0.jpg). - If progress bars aren't updating, ensure your server supports chunked uploads (most modern servers do by default).
内容的提问来源于stack exchange,提问作者Massimo Vantaggio

