Zmiana rozmiaru obrazów w locie za pomocą Nginx i LuaJIT (OpenResty)

Już od dłuższego czasu, zainspirowany artykułem Dynamiczne zmienianie rozmiaru obrazów zostało skonfigurowane zmienianie rozmiaru obrazów za pomocą ngx_http_image_filter_module i wszystko działało jak trzeba. Jednak pojawił się jeden problem, gdy menedżer potrzebował uzyskać obrazy o dokładnych rozmiarach do przesyłania na niektóre usługi, ponieważ były to ich wymagania techniczne. Na przykład, jeśli mamy oryginał obrazu o rozmiarze 1200×1200, a przy zmienianiu rozmiaru wpisujemy coś w rodzaju ?resize=600×400, to otrzymamy proporcjonalnie pomniejszony obraz po mniejszym boku, o rozmiarze 400×400. Nie można także uzyskać obrazu o większej rozdzielczości (upscale). Tzn. ?resize=1500×1500 zwróci ten sam obraz 1200×1200

Z pomocą przyszło artykuł OpenResty: zamieniamy NGINX w pełnoprawny serwer aplikacji aby zrozumieć, jak działa Nginx z Lua oraz sama biblioteka dla Lua isage/lua-imagick — czyste powiązania Lua z ImageMagick. Dlaczego wybrano takie rozwiązanie, a nie, powiedzmy, coś na pythonie — ponieważ jest to szybko i wygodnie. Nawet nie trzeba tworzyć żadnych plików, wszystko bezpośrednio w konfiguracji Nginx (to nie jest konieczne).

Zatem, czego będziemy potrzebować

Przykłady będą podane na podstawie Debiana.

Instalacja nginx i nginx-extras

apt-get update
apt-get install nginx-extras

Instalacja LuaJIT

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

Instalacja imagemagick

apt-get -y install imagemagick

i biblioteki magickwand do niego, w moim przypadku dla wersji 6

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

Kompilacja lua-imagick

Sklonuj repozytorium (lub pobierz zip i rozpakuj)

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

Jeśli wszystko przebiegło pomyślnie, można konfigurować Nginx.

Podam przykład konfiguracji backendu hosta, który zajmuje się zmienianiem rozmiaru. Jest on proxy przez serwer frontowy również z Nginx, na którym odbywa się caching na pewien czas (doba) i inne rzeczy.

konfiguracja backendu nginx

# 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 było potrzebne (rozszerzanie obrazu na krawędziach) odbywa się za pomocą img:extent() i jest definiowane za pomocą parametru resize ze znakiem + na końcu.

Dostępne są następujące parametry:

  • WxH (Zachowaj proporcje, użyj wyższego wymiaru)
  • WxH^ (Zachowaj proporcje, użyj niższego wymiaru (przytnij))
  • WxH! (Ignoruj proporcje)
  • WxH+ (Zachowaj proporcje, dodaj boczne marginesy)

Tabela podsumowująca wyniki zmiany rozmiaru

Parametr uri zapytania
Rozmiar wyjściowego obrazu

?resize=400×200
200×200

?resize=400×200^
400×400

?resize=400×200!
400×200 (Nie proporcjonalnie)

?resize=400×200+
400×200 (Proporcjonalnie)

Zmiana rozmiaru obrazów w locie za pomocą Nginx i LuaJIT (OpenResty)

Podsumowanie

Biorąc pod uwagę całą moc i prostotę tego podejścia, można zrealizować zadania o dość skomplikowanej logice, na przykład dodawanie znaków wodnych lub wdrażanie autoryzacji z ograniczonym dostępem. Aby poznać możliwości API do pracy z obrazami, można zajrzeć do dokumentacji biblioteki. isage/lua-imagick

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster