Laravel: Optimize all your public images

You can use the excellent Image Optimizer Laravel package to automatically optimize all your images as a part of your release process or as a once off. I’ve written a Laravel command which will go through your public/ directory recursively and optimize anything it can. I often get around 40-50% savings on images.

Firstly, install the spatie package:

composer require spatie/laravel-image-optimizer

Then create a new file in app/Console/Commands/ called ImageOptimize.php and put this in:

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Cache;
use Spatie\LaravelImageOptimizer\Facades\ImageOptimizer;

class ImageOptimize extends Command
{
    protected $signature = 'images:optimize';
    protected $description = 'Optimize all public images';

    private $extensions = [
        '.png',
        '.gif',
        '.jpg',
    ];

    public function handle(): void
    {
        $this->info('SCANNING..');

        $allPublicFiles = $this->getDirContents(base_path() . '/public');

        $this->info('OPTIMIZING..');

        $totalBefore = $totalAfter = 0;

        foreach ($allPublicFiles as $file) {
            if (strpos($file, 'assets/scss') !== false) {
                continue;
            }
            $extension = substr($file, -4, 4);
            if (in_array($extension, $this->extensions, false)) {
                $before = filesize($file);
                ImageOptimizer::optimize($file);
                clearstatcache();
                $after = filesize($file);

                $this->comment(' - ' . $file . ' (' . $before . 'b -> ' . $after . 'b)');

                $totalBefore += $before;
                $totalAfter += $after;
            }
        }

        $this->info('OPTIMIZE DONE..');
        $this->comment(' - Total before: ' . $totalBefore);
        $this->comment(' - Total after: ' . $totalAfter);
        $this->comment(' - Total saved: ' . ($totalBefore - $totalAfter));
    }

    private function getDirContents($dir, &$results = []): array
    {
        $this->comment(' - ' . $dir);
        $files = scandir($dir);

        foreach ($files as $value) {
            $path = realpath($dir . DIRECTORY_SEPARATOR . $value);
            if (!is_dir($path)) {
                $results[] = $path;
            } elseif ($value !== '.' && $value !== '..') {
                $this->getDirContents($path, $results);
                $results[] = $path;
            }
        }

        return $results;
    }
}

You can see where you would change the file extensions to look for, and then run the Laravel command:

php artisan images:optimize

The last thing to mention is that for this to work you need to install the various requirements for the Image Optimize package. This is a set of steps for Ubuntu:

sudo apt-get update
sudo apt-get install -y jpegoptim
sudo apt-get install -y optipng
sudo apt-get install -y pngquant
sudo npm install -g svgo
sudo apt-get install -y gifsicle
sudo apt-get install -y webp