You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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.

1. Frontend Implementation (HTML + JavaScript)

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>
2. Backend PHP Implementation (Modular)

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);
?>
3. Key Modular Features Explained
  • 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!
4. Setup & Testing Tips
  1. Create an uploads folder in the same directory as your HTML and PHP files, and set permissions to 0755 (or 0777 if needed, but 0755 is more secure).
  2. Test with different file types to make sure invalid ones are rejected.
  3. Check the uploads folder to confirm files are renamed correctly (e.g., custom_upload_0.jpg).
  4. If progress bars aren't updating, ensure your server supports chunked uploads (most modern servers do by default).

内容的提问来源于stack exchange,提问作者Massimo Vantaggio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 04:02:23