РСсайз ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ Π½Π° Π»Π΅Ρ‚Ρƒ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ Nginx ΠΈ LuaJIT (OpenResty)

Π£ΠΆΠ΅ довольно Π΄Π°Π²Π½ΠΎ, Π²Π΄ΠΎΡ…Π½ΠΎΠ²ΠΈΠ²ΡˆΠΈΡΡŒ ΡΡ‚Π°Ρ‚ΡŒΠ΅ΠΉ РСсайз ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ Π½Π° Π»Π΅Ρ‚Ρƒ Π±Ρ‹Π» настроСн рСсайз ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ ngx_http_image_filter_module ΠΈ всС Ρ€Π°Π±ΠΎΡ‚Π°Π»ΠΎ ΠΊΠ°ΠΊ Π½Π°Π΄ΠΎ. Но появилась ΠΎΠ΄Π½Π° ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ°, ΠΊΠΎΠ³Π΄Π° ΠΌΠ΅Π½Π΅Π΄ΠΆΠ΅Ρ€Ρƒ понадобилось ΠΏΠΎΠ»ΡƒΡ‡Π°Ρ‚ΡŒ изобраТСния с Ρ‚ΠΎΡ‡Π½Ρ‹ΠΌΠΈ Ρ€Π°Π·ΠΌΠ΅Ρ€Π°ΠΌΠΈ для Π·Π°Π»ΠΈΠ²ΠΊΠΈ Π½Π° Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ сСрвисы, Ρ‚.ΠΊ. это Π±Ρ‹Π»ΠΈ ΠΈΡ… тСхничСскиС трСбования. К ΠΏΡ€ΠΈΠΌΠ΅Ρ€Ρƒ, Ссли ΠΌΡ‹ ΠΈΠΌΠ΅Π΅ΠΌ ΠΎΡ€ΠΈΠ³ΠΈΠ½Π°Π» изобраТСния Ρ€Π°Π·ΠΌΠ΅Ρ€ΠΎΠΌ 1200×1200, ΠΈ ΠΏΡ€ΠΈ рСсайзС ΠΌΡ‹ пишСм Ρ‡Ρ‚ΠΎ-Ρ‚ΠΎ Π²Ρ€ΠΎΠ΄Π΅ ?resize=600×400, Ρ‚ΠΎ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠΌ ΠΏΡ€ΠΎΠΏΠΎΡ€Ρ†ΠΈΠΎΠ½Π°Π»ΡŒΠ½ΠΎ ΡƒΠΌΠ΅Π½ΡŒΡˆΠ΅Π½Π½ΠΎΠ΅ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ ΠΏΠΎ Π½Π°ΠΈΠΌΠ΅Π½ΡŒΡˆΠ΅ΠΌΡƒ ΠΊΡ€Π°ΡŽ, Ρ€Π°Π·ΠΌΠ΅Ρ€ΠΎΠΌ 400×400. Π’Π°ΠΊ ΠΆΠ΅ Π½Π΅Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ с бОльшим Ρ€Π°Π·Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅ΠΌ (upscale). Π’.Π΅. ?resize=1500×1500 Π²Π΅Ρ€Π½Π΅Ρ‚ всС Ρ‚ΠΎΠΆΠ΅ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ 1200×1200

На ΠΏΠΎΠΌΠΎΡ‰ΡŒ ΠΏΡ€ΠΈΡˆΠ»Π° ΡΡ‚Π°Ρ‚ΡŒΡ OpenResty: ΠΏΡ€Π΅Π²Ρ€Π°Ρ‰Π°Π΅ΠΌ NGINX Π² ΠΏΠΎΠ»Π½ΠΎΡ†Π΅Π½Π½Ρ‹ΠΉ сСрвСр ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΉ для понимания ΠΊΠ°ΠΊ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ Nginx с Lua ΠΈ сама Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ° для Lua isage/lua-imagick β€” Lua pure-c bindings to ImageMagick. ΠŸΠΎΡ‡Π΅ΠΌΡƒ Π±Ρ‹Π»ΠΎ Π²Ρ‹Π±Ρ€Π°Π½ΠΎ Ρ‚Π°ΠΊΠΎΠ΅ Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅, Π° Π½Π΅, скаТСм, Ρ‡Ρ‚ΠΎ-Π½ΠΈΠ±ΡƒΠ΄ΡŒ Π½Π° python β€” ΠΏΠΎΡ‚ΠΎΠΌΡƒ Ρ‡Ρ‚ΠΎ это быстро ΠΈ ΡƒΠ΄ΠΎΠ±Π½ΠΎ. Π’Π°ΠΌ Π΄Π°ΠΆΠ΅ Π½Π΅ понадобится ΡΠΎΠ·Π΄Π°Π²Π°Ρ‚ΡŒ Π½ΠΈΠΊΠ°ΠΊΠΈΡ… Ρ„Π°ΠΉΠ»ΠΎΠ², всС прямо Π² ΠΊΠΎΠ½Ρ„ΠΈΠ³Π΅ Nginx (Π½Π΅ ΠΎΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎ).

Π˜Ρ‚Π°ΠΊ, Ρ‡Ρ‚ΠΎ Π½Π°ΠΌ понадобится

ΠŸΡ€ΠΈΠΌΠ΅Ρ€Ρ‹ Π±ΡƒΠ΄ΡƒΡ‚ ΠΏΡ€ΠΈΠ²Π΅Π΄Π΅Π½Ρ‹ Π½Π° Π±Π°Π·Π΅ Debian.

Установка nginx ΠΈ nginx-extras

apt-get update
apt-get install nginx-extras

Установка LuaJIT

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

Установка imagemagick

apt-get -y install imagemagick

ΠΈ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ magickwand ΠΊ Π½Π΅ΠΌΡƒ, Π² ΠΌΠΎΠ΅ΠΌ случаС для 6 вСрсии

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

Π‘Π±ΠΎΡ€ΠΊΠ° lua-imagick

ΠšΠ»ΠΎΠ½ΠΈΡ€ΡƒΠ΅ΠΌ Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΉ (Π½Ρƒ ΠΈΠ»ΠΈ Π·Π°Π±ΠΈΡ€Π°Π΅ΠΌ zip ΠΈ распаковываСм)

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

Если всС ΠΏΡ€ΠΎΡˆΠ»ΠΎ ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎ, ΠΌΠΎΠΆΠ½ΠΎ Π½Π°ΡΡ‚Ρ€Π°ΠΈΠ²Π°Ρ‚ΡŒ Nginx.

ΠŸΡ€ΠΈΠ²Π΅Π΄Ρƒ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ ΠΊΠΎΠ½Ρ„ΠΈΠ³Π° backend хоста, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ, собствСнно, ΠΈ занимаСтся рСсайзом. Он проксируСтся Ρ„Ρ€ΠΎΠ½Ρ‚ сСрвСром Ρ‚Π°ΠΊ ΠΆΠ΅ с Nginx, Π½Π° ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ происходит ΠΊΡΡˆΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ Π½Π° Π½Π°ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ врСмя (сутки) ΠΈ ΠΏΡ€. Π²Π΅Ρ‰ΠΈ.

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
    }
}

Π’ΠΎ, Ρ‡Ρ‚ΠΎ Ρ‚Ρ€Π΅Π±ΠΎΠ²Π°Π»ΠΎΡΡŒ (Ρ€Π°ΡΡˆΠΈΡ€Π΅Π½ΠΈΠ΅ изобраТСния ΠΏΠΎ краям) происходит с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ img:extent() ΠΈ опрСдСляСтся с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π° resize со Π·Π½Π°ΠΊΠΎΠΌ + Π² ΠΊΠΎΠ½Ρ†Π΅.

Доступны ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠ΅ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹:

  • WxH (Keep aspect-ratio, use higher dimension)
  • WxH^ (Keep aspect-ratio, use lower dimension (crop))
  • WxH! (Ignore aspect-ratio)
  • WxH+ (Keep aspect-ratio, add side borders)

Бводная Ρ‚Π°Π±Π»ΠΈΡ†Π° с Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π°ΠΌΠΈ рСсайза

ΠŸΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ uri запроса
Π Π°Π·ΠΌΠ΅Ρ€ Π²Ρ‹Ρ…ΠΎΠ΄Π½ΠΎΠ³ΠΎ изобраТСния

?resize=400×200
200×200

?resize=400×200^
400×400

?resize=400×200!
400×200 (НС ΠΏΡ€ΠΎΠΏΠΎΡ€Ρ†ΠΈΠΎΠ½Π°Π»ΡŒΠ½ΠΎ)

?resize=400×200+
400×200 (ΠŸΡ€ΠΎΠΏΠΎΡ€Ρ†ΠΈΠΎΠ½Π°Π»ΡŒΠ½ΠΎ)

РСсайз ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ Π½Π° Π»Π΅Ρ‚Ρƒ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ Nginx ΠΈ LuaJIT (OpenResty)

Π˜Ρ‚ΠΎΠ³

Учитывая всю ΠΌΠΎΡ‰ΡŒ ΠΈ простоту Ρ‚Π°ΠΊΠΎΠ³ΠΎ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄Π° ΠΌΠΎΠΆΠ½ΠΎ Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π²Π΅Ρ‰ΠΈ с довольно слоТной Π»ΠΎΠ³ΠΈΠΊΠΎΠΉ, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€ Π΄ΠΎΠ±Π°Π²Π»ΡΡ‚ΡŒ watermark’ΠΈ ΠΈΠ»ΠΈ Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π°Π²Ρ‚ΠΎΡ€ΠΈΠ·Π°Ρ†ΠΈΡŽ с Ρ€Π°Π·Π³Ρ€Π°Π½ΠΈΡ‡Π΅Π½Π½Ρ‹ΠΌ доступом. Для Ρ‚ΠΎΠ³ΠΎ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΡƒΠ·Π½Π°Ρ‚ΡŒ возмоТности API для Ρ€Π°Π±ΠΎΡ‚Ρ‹ с изобраТСниями, ΠΌΠΎΠΆΠ½ΠΎ ΠΎΠ±Ρ€Π°Ρ‚ΠΈΡ‚ΡŒΡΡ ΠΊ Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π°Ρ†ΠΈΠΈ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ isage/lua-imagick

Π˜ΡΡ‚ΠΎΡ‡Π½ΠΈΠΊ: habr.com