spatie/laravel-image-optimizer
Laravel integration for spatie/image-optimizer. Optimize PNG, JPG, SVG, and GIF files by running them through available system binaries. Use the ImageOptimizer facade, resolve an OptimizerChain from the container, or apply middleware to optimize uploads automatically.
Installation:
composer require spatie/laravel-image-optimizer
Run migrations if using the optional optimized_images table:
php artisan migrate
First Use Case: Optimize an image file directly:
use Spatie\ImageOptimizer\OptimizerChain;
$optimizer = app(OptimizerChain::class);
$optimizer->optimize(storage_path('app/public/image.jpg'));
Verify Tools: Check installed optimization tools via:
php artisan image-optimizer:check
jpegoptim, pngquant).php artisan vendor:publish --provider="Spatie\ImageOptimizer\ImageOptimizerServiceProvider"
Key settings: default_optimizer, allowed_optimizers, and optimize_on_demand.On-Demand Optimization:
// Optimize a single file
$optimizer->optimize($filePath);
// Optimize and save to a new path
$optimizer->optimize($sourcePath, $destinationPath);
Batch Processing:
Use Laravel’s Artisan::command to process directories:
use Spatie\ImageOptimizer\OptimizerChain;
use Illuminate\Support\Facades\File;
$optimizer = app(OptimizerChain::class);
$files = File::allFiles(storage_path('app/public/images'));
foreach ($files as $file) {
$optimizer->optimize($file->pathname());
}
Middleware for Automatic Optimization:
Attach to handle in App\Http\Middleware\OptimizeImages:
public function handle($request, Closure $next) {
$response = $next($request);
if ($response->headers->get('Content-Type') === 'image/jpeg') {
$path = storage_path('app/public/' . $response->getOriginalContent());
$optimizer = app(OptimizerChain::class);
$optimizer->optimize($path);
}
return $response;
}
Queue Optimization Jobs: Dispatch a job for async processing:
use Spatie\ImageOptimizer\Jobs\OptimizeImage;
OptimizeImage::dispatch($filePath)->onQueue('optimize');
Storage Integration:
Use Storage::disk('public')->put() with optimized files to leverage Laravel’s filesystem.
Example:
$optimizer->optimize($source, $destination);
Storage::disk('public')->put($destination, file_get_contents($destination));
Event-Based Optimization:
Listen for eloquent.saved or eloquent.updated events to optimize images tied to models:
public function handle($model) {
if ($model->image) {
$optimizer = app(OptimizerChain::class);
$optimizer->optimize($model->image_path);
}
}
Custom Optimizer Chains:
Extend OptimizerChain to add pre/post-processing:
use Spatie\ImageOptimizer\OptimizerChain;
class CustomOptimizerChain extends OptimizerChain {
protected function getDefaultOptimizers(): array {
return array_merge(parent::getDefaultOptimizers(), ['custom-tool']);
}
}
Bind it in AppServiceProvider:
$this->app->bind(
Spatie\ImageOptimizer\OptimizerChain::class,
fn($app) => new CustomOptimizerChain()
);
Missing Optimization Tools:
NoOptimizerAvailableException or silent failures.sudo apt-get install jpegoptim pngquant) or configure allowed_optimizers in config/image-optimizer.php to skip checks.php artisan image-optimizer:check to verify installed tools.File Permissions:
www-data) has write permissions to the target directory:
chmod -R 775 storage/app/public
chown -R www-data:www-data storage/app/public
SVG/GIF Limitations:
svgo (for SVGs) or gifsicle (for GIFs) is installed. Configure allowed_optimizers to exclude unsupported formats if needed.Race Conditions in Async Jobs:
if (!file_exists($lockFile = $filePath . '.lock')) {
file_put_contents($lockFile, '');
$optimizer->optimize($filePath);
unlink($lockFile);
}
Log Optimization Steps:
Enable debug mode in config/image-optimizer.php:
'debug' => env('IMAGE_OPTIMIZER_DEBUG', false),
Check logs for tool execution details.
Verify Output: Compare file sizes before/after optimization:
$before = filesize($filePath);
$optimizer->optimize($filePath);
$after = filesize($filePath);
logger()->info("Optimized {$filePath}: {$before} -> {$after} bytes");
optimize_on_demand:
true in config/image-optimizer.php to optimize files only when accessed (requires middleware or caching layer).Custom Tool Paths:
'tools' => [
'jpegoptim' => '/custom/path/to/jpegoptim',
],
Fallback Optimizers:
getDefaultOptimizers() if primary tools fail:
protected function getDefaultOptimizers(): array {
return ['jpegoptim', 'pngquant', 'fallback-tool'];
}
Custom Optimizer Classes:
Implement Spatie\ImageOptimizer\Optimizers\Optimizer to add new tools:
class MyCustomOptimizer implements Optimizer {
public function optimize(string $filePath): void {
// Custom logic (e.g., call an API)
}
public function supports(string $filePath): bool {
return pathinfo($filePath, PATHINFO_EXTENSION) === 'webp';
}
}
Register it in OptimizerChain:
protected function getDefaultOptimizers(): array {
return ['jpegoptim', 'MyCustomOptimizer'];
}
Pre/Post-Processing Hooks:
Extend OptimizerChain to add logic before/after optimization:
class ExtendedOptimizerChain extends OptimizerChain {
protected function beforeOptimize(string $filePath): void {
// Example: Resize image before optimization
$this->resize($filePath);
}
protected function afterOptimize(string $filePath): void {
// Example: Update database record
DB::table('images')->where('path', $filePath)->update(['optimized' => true]);
}
}
Database Tracking:
Use the optional optimized_images table to log optimization jobs:
use Spatie\ImageOptimizer\Models\OptimizedImage;
$optimized = OptimizedImage::create([
'path' => $filePath,
'original_size' => filesize($filePath),
]);
$optimizer->optimize($filePath);
$optimized->update(['optimized_size' => filesize($filePath)]);
How can I help you explore Laravel packages today?