Image Processing Examples
For uploaded blobs you often don't need any code: with the extension installed,
GET /blobs/{uuid}?width=800&format=webp resizes on demand (see
File Uploads).
This document provides comprehensive examples of image processing in Glueful, powered by the
glueful/media extension and Intervention Image v4.
Basic Operations
Simple Resize
// Using helper function (recommended)
image($context, '/path/to/photo.jpg')
->resize(800, 600)
->save('/path/to/resized.jpg');
// Using the class directly
use Glueful\Extensions\Media\ImageProcessor;
ImageProcessor::make('/path/to/photo.jpg', $context)
->resize(800, 600)
->save('/path/to/resized.jpg');
Maintain Aspect Ratio
// Resize by width (height auto-calculated)
image($context, '/path/to/photo.jpg')
->resize(800, null) // maintain aspect ratio
->save('/path/to/resized.jpg');
// Or resize by height (width auto-calculated)
image($context, '/path/to/photo.jpg')
->resize(null, 600) // maintain aspect ratio
->save('/path/to/resized.jpg');
Quality Control
// Set JPEG quality (1-100)
image($context, '/path/to/photo.jpg')
->resize(800, 600)
->quality(85)
->save('/path/to/output.jpg');
// Format conversion with quality
image($context, '/path/to/photo.png')
->format('webp')
->quality(80)
->save('/path/to/output.webp');
Advanced Operations
Cropping and Fitting
// Crop to exact dimensions from center
image($context, '/path/to/photo.jpg')
->crop(400, 300, 100, 50) // width, height, x, y
->save('/path/to/cropped.jpg');
// Fit to exact dimensions (covers area and may crop edges)
image($context, '/path/to/photo.jpg')
->fit(800, 600)
->save('/path/to/fitted.jpg');
Watermarking
// Add watermark to bottom-right
image($context, '/path/to/photo.jpg')
->watermark('/path/to/logo.png', 'bottom-right', 50) // 50% opacity
->save('/path/to/watermarked.jpg');
// Different watermark position
image($context, '/path/to/photo.jpg')
->watermark('/path/to/logo.png', 'top-left', 75)
->save('/path/to/watermarked.jpg');
Custom Modifications
// Apply custom Intervention operations via modify()
image($context, '/path/to/photo.jpg')
->modify(function ($img) {
// Example: adjust brightness/contrast using Intervention methods
// $img->brightness(20)->contrast(15);
})
->save('/path/to/adjusted.jpg');
Caching Examples
Basic Caching
// Cache processed image for 24 hours
$imageData = image($context, '/path/to/photo.jpg')
->resize(800, 600)
->quality(85)
->cached('photo-800x600', 86400)
->getImageData();
Cache Key Generation
// Automatic cache key based on operations
$processor = image($context, '/path/to/photo.jpg')
->resize(800, 600)
->quality(85)
->cached(); // Auto-generates cache key
// Custom cache key with TTL
$processor = image($context, '/path/to/photo.jpg')
->resize(800, 600)
->cached('custom-thumbnail-' . $userId, 3600);
Retrieve From Cache (and Serve)
use Glueful\Cache\CacheStore;
// Resolve cache store
/** @var CacheStore<mixed> $cache */
$cache = app($context, 'cache.store');
// Compose full cache key used by ImageProcessor::cached()
$prefix = (string) (config($context, 'image.cache.prefix', 'image_'));
$key = $prefix . 'photo-800x600';
// Try to serve from cache first
if ($cached = $cache->get($key)) {
header('Content-Type: ' . $cached['mime_type']);
echo $cached['image_data'];
return; // done
}
// Cache miss: process and cache
$imageData = image($context, '/path/to/photo.jpg')
->resize(800, 600)
->quality(85)
->cached('photo-800x600', 86400)
->getImageData();
header('Content-Type: image/jpeg');
echo $imageData;
Invalidate Cached Variant
$prefix = (string) (config($context, 'image.cache.prefix', 'image_'));
$key = $prefix . 'photo-800x600';
app($context, 'cache.store')->delete($key);
// Note: cached() stores the processed result under a cache key; retrieval and // invalidation should be handled by your cache layer if needed.
Remote Image Processing
Remote fetching is off by default (fail closed). Enable it and allow-list the hosts:
IMAGE_DISABLE_EXTERNAL_URLS=false
IMAGE_ALLOWED_DOMAINS=example.com,cdn.example.com
Every hop of a fetch is checked against the allow-list, and hosts that resolve to private, loopback or link-local addresses are refused.
Basic Remote Processing
// Process remote image with security checks
try {
$result = image($context, 'https://example.com/photo.jpg')
->resize(800, 600)
->quality(85)
->cached('remote-photo-800x600')
->save('/path/to/local.jpg');
} catch (\Glueful\Http\Exceptions\Domain\BusinessLogicException $e) {
// Handle validation/security/processing errors
echo "Image error: " . $e->getMessage();
}
Batch Processing
// Process multiple images efficiently
$urls = [
'https://example.com/photo1.jpg',
'https://example.com/photo2.jpg',
'https://example.com/photo3.jpg'
];
foreach ($urls as $index => $url) {
try {
image($context, $url)
->resize(400, 300)
->cached("batch-photo-{$index}")
->save("/path/to/batch_{$index}.jpg");
} catch (Exception $e) {
// Log error and continue
error_log("Failed to process {$url}: " . $e->getMessage());
}
}
Working with Uploads
Glueful requests are Symfony requests, so uploaded files are Symfony\Component\HttpFoundation\File\UploadedFile
objects ($request->files). Process them by path — fromUpload() expects a PSR-7
UploadedFileInterface and is not what a Glueful controller receives.
Process File Uploads
use Glueful\Http\Response;
use Glueful\Storage\StorageManager;
use Symfony\Component\HttpFoundation\Request;
public function uploadImage(Request $request): Response
{
$uploadedFile = $request->files->get('image');
if ($uploadedFile === null || !$uploadedFile->isValid()) {
return Response::error('No image uploaded', 422);
}
try {
$processedData = image($this->getContext(), $uploadedFile->getPathname())
->resize(1200, 800)
->quality(85)
->format('webp')
->getImageData();
$filename = 'images/processed_' . time() . '.webp';
app($this->getContext(), StorageManager::class)->put($filename, $processedData);
return Response::success(['filename' => $filename]);
} catch (\Glueful\Http\Exceptions\Domain\BusinessLogicException $e) {
return Response::error('Image processing failed: ' . $e->getMessage(), 422);
}
}
Generate Multiple Sizes
$uploadedFile = $request->files->get('image');
$processor = image($this->getContext(), $uploadedFile->getPathname());
$storage = app($this->getContext(), StorageManager::class);
$sizes = [
'thumbnail' => [150, 150],
'medium' => [400, 300],
'large' => [800, 600],
'original' => [1200, 900]
];
$results = [];
foreach ($sizes as $size => $dimensions) {
$processed = $processor->clone()
->fit($dimensions[0], $dimensions[1])
->quality(85)
->getImageData();
$filename = "images/{$size}_" . time() . '.jpg';
$storage->put($filename, $processed);
$results[$size] = $filename;
}
To also record the upload as a blob (with EXIF stripping and validation), store the original
through FileUploader::uploadMedia() first — see File Uploads.
Performance Optimization
Memory Efficient Processing
// Process large images efficiently
$processor = image($context, '/path/to/large-image.jpg');
// Check dimensions before processing
if ($processor->getWidth() > 4000 || $processor->getHeight() > 4000) {
// Resize very large images first
$processor->resize(2000, 1500);
}
// Continue with normal processing
$result = $processor
->resize(800, 600)
->quality(85)
->save('/path/to/output.jpg');
Streaming for Large Files
// Stream large processed images
public function serveProcessedImage(string $path): void
{
$processor = image($this->getContext(), $path)
->resize(1200, 800)
->cached();
// Stream directly to output
$processor->stream([
'Content-Disposition' => 'inline; filename="processed.jpg"'
]);
}
Error Handling
Comprehensive Error Handling
try {
$result = image($context, '/path/to/photo.jpg')
->resize(800, 600)
->quality(85)
->save('/path/to/output.jpg');
} catch (\Glueful\Http\Exceptions\Domain\BusinessLogicException $e) {
// Validation/security/processing errors (format, size, dimensions, etc.)
error_log("Image error: " . $e->getMessage());
} catch (Exception $e) {
// Generic errors
error_log("Unexpected error: " . $e->getMessage());
}
Configuration Examples
Environment Variables
# Production settings
IMAGE_DRIVER=imagick
IMAGE_MAX_WIDTH=2048
IMAGE_MAX_HEIGHT=2048
IMAGE_MAX_FILESIZE=10M
IMAGE_JPEG_QUALITY=85
IMAGE_WEBP_QUALITY=80
IMAGE_CACHE_ENABLED=true
IMAGE_CACHE_TTL=86400
IMAGE_VALIDATE_MIME=true
IMAGE_CHECK_INTEGRITY=true
IMAGE_DISABLE_EXTERNAL_URLS=false # default true: remote fetching off
IMAGE_ALLOWED_DOMAINS=example.com,cdn.example.com # required for any remote host
Custom Configuration in Services
use Glueful\Extensions\Media\ImageProcessor;
// Custom image processor with specific settings
$processor = new ImageProcessor(
$imageManager,
$cache,
$security,
$logger,
[
'optimization' => [
'jpeg_quality' => 90,
'webp_quality' => 85,
],
'security' => [
'allowed_domains' => ['trusted-cdn.com'],
'validate_mime' => true,
],
'cache' => [
'enabled' => true,
'ttl' => 3600,
'prefix' => 'custom-images-',
]
]
);
Integration with Controllers
RESTful Image API
class ImageController extends BaseController
{
public function resize(Request $request): Response
{
$this->requirePermission('images.process');
// $url must be on IMAGE_ALLOWED_DOMAINS (remote fetching is off by default)
$url = (string) $request->query->get('url');
$width = $request->query->getInt('width', 800);
$height = $request->query->getInt('height', 600);
$quality = $request->query->getInt('quality', 85);
try {
$imageData = image($this->getContext(), $url)
->resize($width, $height)
->quality($quality)
->cached("resize-{$width}x{$height}-q{$quality}")
->getImageData();
return Response::success([
'processed' => true,
'size' => strlen($imageData),
'data' => base64_encode($imageData)
]);
} catch (\Glueful\Http\Exceptions\Domain\BusinessLogicException $e) {
return Response::error('Processing failed: ' . $e->getMessage(), 422);
}
}
}
This completes the comprehensive examples for image processing with the glueful/media extension.