基于PHP+Imagemagick的多图片处理表单改造需求
Hey there! Since you're new to PHP, let's take your existing single-image uploader and turn it into a robust batch processor. We'll cover:
- Multi-file uploads
- Adding
_Tto processed filenames - ZIP archive support
- Optional local directory processing
Let's dive in step by step.
1. Update the Form for Multi-File & ZIP Support
First, modify form.php to let users select multiple images, upload a ZIP, or optionally specify a local directory. We'll also update the session display to show all processed images.
<?php session_start(); ?> <!DOCTYPE html> <html> <head> <title>Batch Image Processor</title> </head> <body> <?php // Display status messages if (isset($_SESSION['message']) && $_SESSION['message']) { printf('<p><b>%s</b></p>', $_SESSION['message']); unset($_SESSION['message']); } // Display all processed images if (isset($_SESSION['final_files']) && !empty($_SESSION['final_files'])) { echo '<h3>Processed Images:</h3>'; foreach ($_SESSION['final_files'] as $file) { printf('<img src="%s" style="max-width: 200px; margin: 10px;" />', $file); echo '<br>'; } unset($_SESSION['final_files']); } ?> <form method="POST" action="process.php" enctype="multipart/form-data"> <h4>Option 1: Upload Multiple Images</h4> <div> <span>Select images to process:</span> <input type="file" name="uploadedFiles[]" multiple accept="image/*" /> </div> <h4>Option 2: Upload a ZIP of Images</h4> <div> <span>Upload ZIP archive:</span> <input type="file" name="uploadedZip" accept=".zip" /> </div> <h4>Option 3: Process Local Directory</h4> <div> <span>Local directory path (e.g., ./local_images/):</span> <input type="text" name="localDir" placeholder="./local_images/" /> </div> <br> <input type="submit" name="processBtn" value="Process Images" /> </form> </body> </html>
Key changes:
- Added
multipleto the file input and renamed it touploadedFiles[]to accept multiple images - Added fields for ZIP upload and local directory input
- Updated the session display to loop through all processed files
2. Rewrite process.php for Batch Processing
Next, update process.php to handle multiple files, ZIP archives, and local directories. We'll generate output filenames with _T appended instead of using fixed names.
<?php session_start(); require_once 'process-lib.php'; $imageProcessor = new ImageProcessor(); $message = ''; $processedFiles = []; // Create upload directory if it doesn't exist $uploadDir = './uploaded_files/'; if (!is_dir($uploadDir)) { mkdir($uploadDir, 0755, true); } // Handle multiple image uploads if (isset($_POST['processBtn']) && isset($_FILES['uploadedFiles']) && $_FILES['uploadedFiles']['error'][0] !== UPLOAD_ERR_NO_FILE) { $files = $_FILES['uploadedFiles']; for ($i = 0; $i < count($files['name']); $i++) { if ($files['error'][$i] === UPLOAD_ERR_OK) { $fileTmpPath = $files['tmp_name'][$i]; $fileName = $files['name'][$i]; $fileExtension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION)); $allowedExts = ['jpg', 'gif', 'png', 'jpeg']; if (in_array($fileExtension, $allowedExts)) { // Generate output filename: original_name_T.ext $baseName = pathinfo($fileName, PATHINFO_FILENAME); $outputFileName = $baseName . "_T." . $fileExtension; $destPath = $uploadDir . $fileName; $outputPath = $uploadDir . $outputFileName; if (move_uploaded_file($fileTmpPath, $destPath)) { $imageProcessor->convert($destPath, $outputPath); $processedFiles[] = $outputPath; } else { $message .= "Error moving file: $fileName<br>"; } } else { $message .= "Skipping non-image file: $fileName<br>"; } } } } // Handle ZIP upload if (isset($_POST['processBtn']) && isset($_FILES['uploadedZip']) && $_FILES['uploadedZip']['error'] === UPLOAD_ERR_OK) { $zipTmpPath = $_FILES['uploadedZip']['tmp_name']; $zip = new ZipArchive(); if ($zip->open($zipTmpPath) === true) { $extractDir = $uploadDir . 'extracted_' . time() . '/'; mkdir($extractDir); $zip->extractTo($extractDir); $zip->close(); // Scan extracted directory for images $imageFiles = glob($extractDir . '*.[jJ][pP][gG]'); $imageFiles = array_merge($imageFiles, glob($extractDir . '*.[pP][nN][gG]')); $imageFiles = array_merge($imageFiles, glob($extractDir . '*.[gG][iI][fF]')); foreach ($imageFiles as $imgPath) { $fileName = basename($imgPath); $baseName = pathinfo($fileName, PATHINFO_FILENAME); $fileExtension = pathinfo($fileName, PATHINFO_EXTENSION); $outputPath = $uploadDir . $baseName . "_T." . $fileExtension; $imageProcessor->convert($imgPath, $outputPath); $processedFiles[] = $outputPath; } // Optional: Clean up extracted directory // rmdir($extractDir); // Uncomment if you want to delete after processing } else { $message .= "Failed to open ZIP file<br>"; } } // Handle local directory processing if (isset($_POST['processBtn']) && !empty($_POST['localDir'])) { $localDir = rtrim($_POST['localDir'], '/') . '/'; if (is_dir($localDir) && is_readable($localDir)) { $imageFiles = glob($localDir . '*.[jJ][pP][gG]'); $imageFiles = array_merge($imageFiles, glob($localDir . '*.[pP][nN][gG]')); $imageFiles = array_merge($imageFiles, glob($localDir . '*.[gG][iI][fF]')); foreach ($imageFiles as $imgPath) { $fileName = basename($imgPath); $baseName = pathinfo($fileName, PATHINFO_FILENAME); $fileExtension = pathinfo($fileName, PATHINFO_EXTENSION); $outputPath = $uploadDir . $baseName . "_T." . $fileExtension; $imageProcessor->convert($imgPath, $outputPath); $processedFiles[] = $outputPath; } } else { $message .= "Invalid or unreadable local directory<br>"; } } // Set session messages and redirect if (!empty($processedFiles)) { $message .= "Successfully processed " . count($processedFiles) . " images<br>"; $_SESSION['final_files'] = $processedFiles; } $_SESSION['message'] = $message; header("Location: form.php"); ?>
Key changes:
- Added loops to handle multiple uploaded files
- Added ZIP archive extraction using
ZipArchive(ensure PHP has this extension enabled) - Added local directory scanning with
glob() - Generated dynamic output filenames with
_Tappended instead of fixedfinal.png - Collected all processed files into an array for display
3. Update ImageProcessor for Dynamic Output Paths
Modify process-lib.php to accept a custom output file path instead of using hardcoded values. We'll also remove the fixed final_file property since we're generating paths dynamically.
<?php class ImageProcessor { private $upload_dir; public function __construct () { $this->upload_dir = 'uploaded_files'; // Create directory if it doesn't exist if (!is_dir($this->upload_dir)) { mkdir($this->upload_dir, 0755, true); } } public function convert ($source_image, $output_file) { // Escape shell arguments to prevent security issues $sourceEscaped = escapeshellarg($source_image); $outputEscaped = escapeshellarg($output_file); // Your ImageMagick command, using dynamic output path $exec = "convert $sourceEscaped -resize '500x500>' -write mpr:img0 \ -set option:distort:viewport '%[fx:max(w,h)]x%[fx:max(w,h)]' \ -distort SRT '%[fx:(w>h)*(w/2)],%[fx:(h>w)*(h/2)] %[fx:max(w/h,h/w)] 0' \ -fill white -colorize 70 mpr:img0 -gravity center -composite $outputEscaped"; // Execute command and capture output/errors exec($exec, $output, $returnVar); // Log errors if command fails if ($returnVar !== 0) { $this->logger("Error processing $source_image: " . implode("\n", $output)); } } public function logger ($message) { // Append timestamp to log entries $logMessage = date('Y-m-d H:i:s') . ": " . $message . "\n"; file_put_contents('uploader.log', $logMessage, FILE_APPEND); } }
Key changes:
- Removed fixed file properties (main_file, final_file, etc.) since we use dynamic paths
- Added
escapeshellarg()to sanitize file paths (critical for security!) - Updated the
convertmethod to accept an$output_fileparameter - Improved logging with timestamps and error capture
Important Notes for Security & Functionality
- Permissions: Make sure the
uploaded_filesdirectory is writable by your web server (chmod 0755 or 0775 usually works) - Shell Security: Always use
escapeshellarg()when passing user-provided paths to shell commands to prevent command injection attacks - ZIP Extraction: The ZIP extraction creates a temporary directory—you might want to add cleanup logic to delete it after processing
- Local Directory Access: Be careful with local directory input—restrict it to specific allowed directories if possible to prevent unauthorized file access
- ImageMagick Path: If
convertisn't found, use the full path to the executable (e.g.,/usr/bin/convert) in your command
内容的提问来源于stack exchange,提问作者Ben Dodson

