Created
January 20, 2026 01:58
-
-
Save khoipro/d7558260bc1a53f471bb25874eff3250 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /** | |
| * Bulk Image Resizer for PHP & MySQL Applications | |
| * Automatically detects Imagick or GD. | |
| * | |
| * @package codetot-optimization | |
| * @author codetot, khoipro, gemini | |
| * @since 0.0.1 | |
| * | |
| * Usage: copy to your project, and run 'php php-bulk-resize-image.php' | |
| * | |
| */ | |
| // --- CONFIGURATION --- | |
| $config = [ | |
| 'source_dir' => __DIR__ . '/uploads/gallery', // Change to your image folder | |
| 'target_width' => 600, // Mobile width | |
| 'suffix' => '-mobile', // Suffix for new files | |
| 'quality' => 75, // 0-100 (75 is best for web) | |
| 'batch_limit' => 500, // Set limit if running via browser | |
| ]; | |
| // --- SERVER TWEAKS --- | |
| ini_set('memory_limit', '512M'); | |
| set_time_limit(0); // Unlimited execution time for CLI | |
| // --- INITIALIZATION --- | |
| $is_cli = (php_sapi_name() === 'cli'); | |
| $has_imagick = extension_loaded('imagick'); | |
| $has_gd = extension_loaded('gd'); | |
| echo $is_cli ? "Starting Bulk Resize...\n" : "<h3>Starting Bulk Resize</h3>"; | |
| if (!$has_imagick && !$has_gd) { | |
| die("Error: Neither Imagick nor GD library is installed."); | |
| } | |
| echo "Using Library: " . ($has_imagick ? "Imagick (High Speed)" : "GD (Standard)") . ($is_cli ? "\n" : "<br>"); | |
| // --- THE PROCESSING LOOP --- | |
| try { | |
| $directory = new RecursiveDirectoryIterator($config['source_dir']); | |
| $iterator = new RecursiveIteratorIterator($directory); | |
| $processed_count = 0; | |
| $skipped_count = 0; | |
| foreach ($iterator as $file) { | |
| if ($file->isDir()) continue; | |
| $path = $file->getPathname(); | |
| $ext = strtolower($file->getExtension()); | |
| // 1. Filter: Only process JPG/PNG | |
| if (!in_array($ext, ['jpg', 'jpeg', 'png'])) continue; | |
| // 2. Filter: Skip if file is already a mobile version | |
| if (strpos($path, $config['suffix']) !== false) continue; | |
| $outputPath = $file->getPath() . '/' . $file->getBasename('.' . $ext) . $config['suffix'] . '.' . $ext; | |
| // 3. Filter: Skip if mobile version already exists (Resume capability) | |
| if (file_exists($outputPath)) { | |
| $skipped_count++; | |
| continue; | |
| } | |
| // --- RESIZE EXECUTION --- | |
| if ($has_imagick) { | |
| processWithImagick($path, $outputPath, $config); | |
| } else { | |
| processWithGD($path, $outputPath, $config, $ext); | |
| } | |
| $processed_count++; | |
| // Progress Feedback | |
| if ($processed_count % 50 === 0) { | |
| echo "Processed: $processed_count files..." . ($is_cli ? "\n" : "<br>"); | |
| // Flush buffer for web view | |
| if (!$is_cli) { @ob_flush(); flush(); } | |
| } | |
| } | |
| echo ($is_cli ? "\nCOMPLETED!" : "<h3>COMPLETED!</h3>"); | |
| echo "New Images Created: $processed_count" . ($is_cli ? "\n" : "<br>"); | |
| echo "Already Existed (Skipped): $skipped_count" . ($is_cli ? "\n" : "<br>"); | |
| } catch (Exception $e) { | |
| die("Fatal Error: " . $e->getMessage()); | |
| } | |
| // --- HELPER FUNCTIONS --- | |
| function processWithImagick($input, $output, $config) { | |
| $img = new Imagick($input); | |
| // Lanczos is a high-quality filter for downscaling | |
| $img->resizeImage($config['target_width'], 0, Imagick::FILTER_LANCZOS, 1); | |
| $img->stripImage(); // Removes EXIF data for smaller file size | |
| $img->setImageCompressionQuality($config['quality']); | |
| $img->writeImage($output); | |
| $img->clear(); | |
| $img->destroy(); | |
| } | |
| function processWithGD($input, $output, $config, $ext) { | |
| if ($ext === 'png') { | |
| $src = @imagecreatefrompng($input); | |
| } else { | |
| $src = @imagecreatefromjpeg($input); | |
| } | |
| if (!$src) return; | |
| $oldW = imagesx($src); | |
| $oldH = imagesy($src); | |
| if ($oldW <= $config['target_width']) { | |
| imagedestroy($src); | |
| return; | |
| } | |
| $newH = floor($oldH * ($config['target_width'] / $oldW)); | |
| $tmp = imagecreatetruecolor($config['target_width'], $newH); | |
| // Transparency for PNG | |
| if ($ext === 'png') { | |
| imagealphablending($tmp, false); | |
| imagesavealpha($tmp, true); | |
| } | |
| imagecopyresampled($tmp, $src, 0, 0, 0, 0, $config['target_width'], $newH, $oldW, $oldH); | |
| if ($ext === 'png') { | |
| imagepng($tmp, $output, 7); // 0-9 scale | |
| } else { | |
| imagejpeg($tmp, $output, $config['quality']); | |
| } | |
| imagedestroy($src); | |
| imagedestroy($tmp); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment