DEV Community

Cover image for Scaling Image Optimization in Laravel 11 Architecture 🖼️
Prajapati Paresh
Prajapati Paresh

Posted on Originally published at smarttechdevs.in

Scaling Image Optimization in Laravel 11 Architecture 🖼️

The User-Generated Payload Crisis

In modern peer-to-peer (P2P) marketplaces, User-Generated Content (UGC) is the driving force of the platform. When we architected the Direct Farmer Marketplace (ખેડૂત બજાર) for KhedutBandhu, we allowed farmers to list used tractors, agricultural land, and livestock directly from their smartphones. This created an immediate, critical infrastructure bottleneck: image handling.

Modern budget Android smartphones capture photos at 12 to 48 megapixels, generating raw JPEG files weighing between 4MB and 10MB. If a farmer attempts to upload five photos of a tractor on a rural 3G connection, a standard Laravel HTTP POST request will attempt to buffer 50MB of data directly into your server's RAM. This blocks PHP-FPM workers, causes devastating upload timeouts, and completely exhausts server memory. Furthermore, serving those unoptimized 10MB images back to buyers will instantly consume their mobile data plans and destroy the platform's layout rendering speeds.

At Smart Tech Devs, we protect our application servers from heavy media payloads by completely bypassing them. We architected a Direct-to-S3 Upload Pipeline combined with asynchronous background image optimization.

Phase 1: Bypassing the Server with Presigned URLs

Instead of routing the heavy image payload through the Laravel backend, we instruct the mobile app to upload the file directly to Amazon S3 (or an S3-compatible storage like Cloudflare R2).

To do this securely without exposing our AWS credentials, the mobile app first asks the Laravel API for a Presigned URL. This is a temporary, cryptographically signed URL that grants the client permission to upload exactly one file to a specific path for a very short duration (e.g., 5 minutes).


namespace App\Http\Controllers\Api;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;

class MarketplaceMediaController
{
    public function generateUploadUrl(Request $request)
    {
        // 1. Generate a secure, randomized path for the future file
        $fileName = Str::uuid() . '.jpg';
        $path = "marketplace/tractors/tmp/{$fileName}";

        // 2. Generate the temporary Presigned URL from the S3 disk
        $s3Client = Storage::disk('s3')->getClient();
        $command = $s3Client->getCommand('PutObject', [
            'Bucket' => config('filesystems.disks.s3.bucket'),
            'Key' => $path,
            'ContentType' => 'image/jpeg',
            'ACL' => 'private', // Keep it private until processed
        ]);

        // 3. The URL expires in exactly 5 minutes
        $presignedRequest = $s3Client->createPresignedRequest($command, '+5 minutes');

        return response()->json([
            'upload_url' => (string) $presignedRequest->getUri(),
            'file_path' => $path // The client will send this back after a successful upload
        ]);
    }
}

Phase 2: Asynchronous Image Optimization

Once the mobile app finishes uploading the image directly to S3, it sends a lightweight HTTP request to Laravel containing only the text data (tractor price, description, and the file_path). Our Laravel server then dispatches a Redis background job to fetch, compress, and organize the image without keeping the user waiting.

We utilize the powerful spatie/laravel-medialibrary combined with the Intervention Image package to handle format conversion.


namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use App\Models\MarketplaceListing;
use Illuminate\Support\Facades\Storage;
use Spatie\Image\Image;

class OptimizeListingImages implements ShouldQueue
{
    use Dispatchable, Queueable;

    public function __construct(
        public int $listingId, 
        public string $tmpFilePath
    ) {}

    public function handle()
    {
        $listing = MarketplaceListing::find($this->listingId);
        
        // 1. Download the raw, unoptimized 10MB image temporarily to the worker
        $rawImageContent = Storage::disk('s3')->get($this->tmpFilePath);
        $localTmpPath = storage_path('app/tmp/' . basename($this->tmpFilePath));
        file_put_contents($localTmpPath, $rawImageContent);

        // 2. Add it to the Spatie Media Library and trigger conversions
        $listing->addMedia($localTmpPath)
            ->withCustomProperties(['optimized' => true])
            ->toMediaCollection('tractor_images', 's3');

        // 3. Delete the original massive file from the temporary S3 folder
        Storage::disk('s3')->delete($this->tmpFilePath);
    }
}

Phase 3: Next-Gen Formats (WebP)

Inside our MarketplaceListing Eloquent model, we define exact conversion parameters. We strip unnecessary EXIF data and convert the bulky JPEG into the highly efficient WebP format, drastically reducing the file size by up to 85% with zero perceivable loss in visual quality.


namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
use Spatie\MediaLibrary\MediaCollections\Models\Media;

class MarketplaceListing extends Model implements HasMedia
{
    use InteractsWithMedia;

    public function registerMediaConversions(Media $media = null): void
    {
        // Generate a tiny thumbnail for list views
        $this->addMediaConversion('thumb')
              ->width(200)
              ->height(200)
              ->format('webp')
              ->nonQueued(); // Processed immediately during the Job

        // Generate a responsive, watermarked image for the detail page
        $this->addMediaConversion('detail')
              ->width(800)
              ->format('webp')
              ->watermark(public_path('images/khedutbandhu-watermark.png'))
              ->nonQueued();
    }
}

The Engineering ROI

By architecting our media pipeline using S3 Presigned URLs and asynchronous WebP conversions, KhedutBandhu achieves limitless scalability. The primary Laravel application is completely shielded from HTTP body buffering attacks and memory exhaustion. Farmers experience zero-latency form submissions because they aren't waiting for the server to process pixels. When a buyer browses the tractor marketplace, they receive lightning-fast, highly optimized WebP images served directly via a Global CDN, saving precious rural bandwidth and guaranteeing flawless scrolling performance.

Top comments (0)