Změna velikosti obrázků za běhu pomocí Nginx a LuaJIT (OpenResty)

Už nějakou dobu, inspirováni článkem Úprava obrázků za běhu bylo nastavováno měření obrázků pomocí ngx_http_image_filter_module a všechno fungovalo jak má. Ale objevil se jeden problém, když manager potřeboval získat obrázky s přesnými rozměry pro nahrání na některé služby, protože to byly jejich technické požadavky. Například, pokud máme originál obrázku velikosti 1200×1200, a při měření píšeme něco jako ?resize=600×400, tak dostaneme proporcionálně zmenšený obrazec podle nejmenšího okraje, velikosti 400×400. Také není možné získat obrázek s vyšším rozlišením (upscale). T.j. ?resize=1500×1500 vrátí stále stejný obrázek 1200×1200

Na pomoc přišla článek OpenResty: proměňujeme NGINX v plnohodnotný aplikační server pro pochopení, jak funguje Nginx s Lua a samotná knihovna pro Lua isage/lua-imagick — Lua pure-c bindings to ImageMagick. Proč bylo vybráno takové řešení, a ne, dejme tomu, něco na python — protože je to rychlé a pohodlné. Nebudete muset vytvářet žádné soubory, vše přímo v konfigu Nginx (není povinné).

Takže, co budeme potřebovat

Příklady budou uvedeny na základě Debianu.

Instalace nginx a nginx-extras

apt-get update
apt-get install nginx-extras

Instalace LuaJIT

apt-get -y install lua5.1 luajit-5.1 libluajit-5.1-dev

Instalace imagemagick

apt-get -y install imagemagick

a knihovny magickwand k němu, v mém případě pro 6. verzi

apt-cache search libmagickwand
apt-get -y install libmagickwand-6.q16-3 libmagickwand-6.q16-dev

Sestavení lua-imagick

Naklonujeme repozitář (nebo vezmeme zip a rozbalíme)

cd ~
git clone https://github.com/isage/lua-imagick.git
cd lua-imagick
mkdir build
cd build
cmake ..
make
make install

Pokud vše proběhlo úspěšně, můžeme konfigurovat Nginx.

Uvedu příklad konfigu backend hosta, který se věnuje měření. Ten je procházen frontend serverem také s Nginx, na kterém dochází k ukládání do cache na určitou dobu (24 hodin) a podobným věcem.

nginx backend config

# Backend image server
server {
    listen       8082;
    listen [::]:8082;
    set $files_root /var/www/example.lh/frontend/web;
    root $files_root;
    access_log off;
    expires 1d;

    location /files {
        # дефолтные значения ресайза
        set $w 700;
        set $h 700;
        set $q 89;

        #1-89 allowed
        if ($arg_q ~ "^([1-9]|[1-8][0-9])$") {
            set $q $arg_q;
        }

        if ($arg_resize ~ "([d-]+)x([d+!^]+)") {  
            set $w $1;
            set $h $2;
            rewrite  ^(.*)$   /resize/$w/$h/$q$uri     last;
        }

        rewrite  ^(.*)$   /resize/$w/$h/$q$uri     last;
    }

    location ~* ^/resize/([d]+)/([d+!^]+)/([d]+)/files/(.+)$ {
        default_type 'text/plain';

        set $w $1;
        set $h $2;
        set $q $3;
        set $fname $4;

        # Есть возможность вынести весь Lua код в отдельный файл
        # content_by_lua_file /var/www/some.lua;
        # lua_code_cache off; #dev
        content_by_lua '
        local magick = require "imagick"
        local img = magick.open(ngx.var.files_root .. "/files/" .. ngx.var.fname)
        if not img then ngx.exit(ngx.HTTP_NOT_FOUND) end
        img:set_gravity(magick.gravity["CenterGravity"])

        if string.match(ngx.var.h, "%d+%+") then
            local h = string.gsub(ngx.var.h, "(%+)", "")
            resize = ngx.var.w .. "x" .. h
            -- для png с альфа каналом
            img:set_bg_color(img:has_alphachannel() and "none" or img:get_bg_color())
            img:smart_resize(resize)
            img:extent(ngx.var.w, h)
        else
                img:smart_resize(ngx.var.w .. "x" .. ngx.var.h)
        end

        if ngx.var.arg_q then img:set_quality(ngx.var.q) end

        ngx.say(img:blob())
        ';
    }
}

# Upstream
upstream imageserver {
    server localhost:8082;
}

server {
    listen 80;
    server_name examaple.lh;

    # отправляем все jpg и png картинки на imageserver
    location ~* ^/files/.+.(jpg|png) {
        proxy_buffers 8 2m;
        proxy_buffer_size 10m;
        proxy_busy_buffers_size 10m;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        proxy_pass     http://imageserver;  # Backend image server
    }
}

To, co bylo požadováno (rozšíření obrázku po okrajích) se děje pomocí img:extent() a určuje se pomocí parametru resize se znaménkem + na konci.

Jsou dostupné následující parametry:

  • WxH (Zachovat poměr stran, použít vyšší rozměr)
  • WxH^ (Zachovat poměr stran, použít nižší rozměr (oříznout))
  • WxH! (Ignorovat poměr stran)
  • WxH+ (Zachovat poměr stran, přidat boční okraje)

Souhrnná tabulka s výsledky měření

Parametr uri požadavku
Velikost výstupního obrázku

?resize=400×200
200×200

?resize=400×200^
400×400

?resize=400×200!
400×200 (Neproporcionálně)

?resize=400×200+
400×200 (Proporcionálně)

Změna velikosti obrázků za běhu pomocí Nginx a LuaJIT (OpenResty)

Výsledek

Při zohlednění veškeré síly a jednoduchosti tohoto přístupu lze realizovat věci se poměrně složitou logikou, například přidávat watermarky nebo realizovat autorizaci s omezeným přístupem. Chcete-li zjistit možnosti API pro práci s obrázky, můžete se obrátit na dokumentaci knihovny. isage/lua-imagick

Zdroj: habr.com

Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster