Automatická optimalizace obrázků pro WordPress

Jedním z největších problémů pro rychlost každého webu jsou zbytečně velké obrázky.

Jako první pomoc na všech svých webech používám skvělý plugin Imsanity, který umožňuje nastavit maximální rozměr nahrávaných obrázků a automaticky je zmenší.

Dlouhou dobu jsem také používal TinyPNG, plugin, který umožňuje opravdu kvalitní kompresi obrázků přes API TinyPNG. Služba funguje skvěle, ale musí se buď nastavovat pro každý web zvlášť, nebo je od určitého množství obrázků placená.

Proto jsem vytvořil jednoduchý plugin, který pro kompresi používá pngquant a jpegoptim.

Jaký je rozdíl mezi těmito obrázky?

Ten vlevo má velikost 2MB, ten vpravo 412Kb, při stejném rozlišení..:-)

Kompresi zajišťuje jednoduchý kód:

<?php
/*
Plugin Name: yotlix.cz - Compress Images
Plugin URI: http://yotlix.cz
Description: This plugin compresses images when uploaded to media library - uses pngquant and jpgotpim
Author: Václav Greif
Version: 1.0
Author URI: http://wp-programator.cz/
*/

class MW_Compress_Images {
    function __construct()
    {
        add_action('wp_generate_attachment_metadata', array($this, 'compress_images'), 10, 2);
    }

    /**
     * Compress images
     * @param $metadata
     * @param $attachment_id
     * @return mixed
     * @throws Exception
     */
    function compress_images($metadata, $attachment_id)
    {
        $images = [];

        // Get main image path
        $image = get_attached_file($attachment_id);

        // Get image path
        $path = dirname($image);

        // Get image MIME type
        $mime = mime_content_type($image);
        
        // Add the image to the array to compress
        $images[$mime][] = $image;
        foreach ($metadata['sizes'] as $size => $data) {
            // Add all image sizes to the array to compress
            $images[$data['mime-type']][] = $path . '/' . $data['file'];
        }

        // Compress PNG
        if (isset($images['image/png'])) {
            foreach ($images['image/png'] as $image) {
                $compressed = $this->compress_png($image, 80);
                file_put_contents($image, $compressed);
            }
        }

        // Compress JPG
        if (isset($images['image/jpeg'])) {
            foreach ($images['image/jpeg'] as $image) {
                $compressed = $this->compress_jpeg($image, 80);
                file_put_contents($image, $compressed);
            }
        }
        return $metadata;
    }

    /**
     * Optimizes PNG file with pngquant 1.8 or later (reduces file size of 24-bit/32-bit PNG images).
     *
     * You need to install pngquant 1.8 on the server (ancient version 1.0 won't work).
     * There's package for Debian/Ubuntu and RPM for other distributions on http://pngquant.org
     *
     * @param $path_to_png_file string - path to any PNG file, e.g. $_FILE['file']['tmp_name']
     * @param $max_quality int - conversion quality, useful values from 60 to 100 (smaller number = smaller file)
     * @return string - content of PNG file after conversion
     */
    function compress_png($path_to_png_file, $max_quality = 90)
    {
        if (!file_exists($path_to_png_file)) {
            throw new Exception("File does not exist: $path_to_png_file");
        }

        // guarantee that quality won't be worse than that.
        $min_quality = 60;

        // '-' makes it use stdout, required to save to $compressed_png_content variable
        // '<' makes it read from the given file path
        // escapeshellarg() makes this safe to use with any path
        $compressed_png_content = shell_exec("pngquant --quality=$min_quality-$max_quality - < ".escapeshellarg(    $path_to_png_file));

        if (!$compressed_png_content) {
            throw new Exception("Conversion to compressed PNG failed. Is pngquant 1.8+ installed on the server?");
        }

        return $compressed_png_content;
    }


    /**
     * Optimizes JPG file with jpegoptim
     *
     * You need to install jpegoptim on the server
     *
     * @param $path_to_png_file string - path to any PNG file, e.g. $_FILE['file']['tmp_name']
     * @param $max_quality int - conversion quality, useful values from 60 to 100 (smaller number = smaller file)
     * @return string - content of PNG file after conversion
     */

    function compress_jpeg($path_to_file, $max_quality = 90)
    {
        if (!file_exists($path_to_file)) {
            throw new Exception("File does not exist: $path_to_file");
        }

        $compressed_content = shell_exec("jpegoptim --max=$max_quality --strip-all --all-progressive - < ".escapeshellarg($path_to_file));

        if (!$compressed_content) {
            throw new Exception("Conversion to compressed PNG failed. Is pngquant 1.8+ installed on the server?");
        }

        return $compressed_content;
    }
}

add_action('init','mw_compress_images_init');
function mw_compress_images_init() {
    new MW_Compress_Images();
}

Přidat komentář