Skip to content

Instantly share code, notes, and snippets.

@khoipro
Created January 20, 2026 01:58
Show Gist options
  • Select an option

  • Save khoipro/d7558260bc1a53f471bb25874eff3250 to your computer and use it in GitHub Desktop.

Select an option

Save khoipro/d7558260bc1a53f471bb25874eff3250 to your computer and use it in GitHub Desktop.
<?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