spatie/image
Expressive PHP image manipulation with a fluent API. Resize, crop, rotate, sharpen, adjust brightness/contrast, apply filters, set quality and orientation, and convert formats. Load, chain operations, and save to a new file or overwrite.
Installation:
composer require spatie/image
Ensure PHP extensions gd, imagick, or vips are enabled (required for drivers).
First Use Case:
use Spatie\Image\Image;
// Load, resize, and save an image
Image::load(public_path('original.jpg'))
->resize(800, 600)
->save(public_path('resized.jpg'));
Key Entry Points:
config/image.php (default: gd if imagick unavailable).load(), resize(), crop(), filter(), save().Dynamic Image Processing in Controllers:
public function uploadImage(Request $request) {
$image = Image::load($request->file('image')->path())
->fit(1200, 800, 'top-left')
->sharpen(10)
->save(public_path('processed/' . $request->file('image')->hashName()));
return response()->json(['path' => $image]);
}
Queue-Based Processing (for large files):
// In controller
ProcessImageJob::dispatch($request->file('image'));
// Job class
public function handle() {
Image::load(storage_path('temp/' . $this->filename))
->resize(1024, null, function ($constraint) {
$constraint->aspectRatio();
})
->save(storage_path('processed/' . $this->filename));
}
Reusable Image Services:
class ImageService {
public function generateThumbnail($path, $width) {
return Image::load($path)
->resize($width, null, function ($constraint) {
$constraint->aspectRatio();
})
->save();
}
}
Integration with Storage:
use Illuminate\Support\Facades\Storage;
$image = Image::load(Storage::path('images/original.jpg'))
->watermark(public_path('watermark.png'), 'bottom-right', 0.2)
->save(Storage::path('images/processed.jpg'));
Batch Processing:
$files = Storage::files('images/raw');
foreach ($files as $file) {
Image::load($file)
->greyscale()
->save(str_replace('raw', 'processed', $file));
}
Conditional Processing:
$image = Image::load($path);
if ($request->has('crop')) {
$image->crop($request->crop['width'], $request->crop['height']);
}
$image->save();
Driver-Specific Optimizations:
// Force Imagick for high-quality operations
Image::load($path)->useDriver('imagick')->resize(2000, null);
Event-Based Processing (e.g., uploaded events):
public function handle(Uploaded $event) {
Image::load($event->file->path())
->fit(1000, 1000)
->save($event->file->path());
}
Driver Dependencies:
pixelate(), focalCrop()), but requires imagick PHP extension.vips extension).Image::load($path)->useDriver('imagick')->resize(...);
File Path Handling:
public_path(), storage_path(), or realpath() to avoid issues.EXIF Orientation:
imagick or manually fix:
Image::load($path)->orientation()->save();
Memory Limits:
memory_limit. Increase it temporarily:
ini_set('memory_limit', '512M');
Image::load($largeFile)->resize(...)->save();
Format-Specific Issues:
imagick or vips.webp support is enabled in GD/Imagick (gd.webp in php.ini).Image::load($path)->resize()->save()->format('webp');
Race Conditions:
$filename = uniqid() . '.jpg';
Image::load($path)->save(public_path("processed/{$filename}"));
Check Driver Availability:
if (!Image::drivers()->has('imagick')) {
throw new \RuntimeException('Imagick driver not available');
}
Validate Image Data:
try {
$image = Image::load($path);
// Process image
} catch (\Spatie\Image\Exceptions\InvalidPath $e) {
Log::error("Invalid image path: {$path}");
}
Log Driver Usage:
Image::load($path)->useDriver('imagick')->on('save', function () {
Log::debug('Saved image with Imagick driver');
})->save();
Test Locally with storage:link:
Ensure processed images are accessible via public symlink:
php artisan storage:link
Cache Processed Images:
$cachePath = storage_path("cache/{$hash}.jpg");
if (!file_exists($cachePath)) {
Image::load($path)->resize()->save($cachePath);
}
Use vips for Large Files:
Image::load($path)->useDriver('vips')->resize()->save();
Disable Unused Filters:
Avoid chaining unnecessary methods (e.g., greyscale()->sharpen() if only resizing is needed).
Custom Drivers:
Implement Spatie\Image\Contracts\Driver for proprietary formats:
class MyDriver implements Driver {
public function load($path) { /* ... */ }
public function save($path) { /* ... */ }
// Implement other methods
}
Register via:
Image::extend('my_driver', function () {
return new MyDriver();
});
Override Default Behavior:
Extend the Image class:
class CustomImage extends \Spatie\Image\Image {
public function customFilter() {
$this->sepia();
return $this;
}
}
Use via:
CustomImage::load($path)->customFilter()->save();
Hooks for Post-Processing:
Use the on() method to inject logic:
Image::load($path)
->on('save', function ($image) {
// Add metadata, log, etc.
})
->resize()
->save();
Default Driver:
Set in config/image.php:
'default' => env('IMAGE_DRIVER', 'gd'),
Override globally:
Image::useDriver('imagick');
Quality Settings:
quality() (1–100, default: 75).quality() (1–100, default: 92).quality() (1–100, default: 90).Format Preservation:
Use format() to enforce output format:
Image::load($path)->resize()->format('webp')->save();
Chaining Without Saving:
// ❌ Avoid: No save() call
Image::load($path)->resize()->crop();
**Ignoring Driver Fail
How can I help you explore Laravel packages today?