Redimensionamiento de imágenes al vuelo con Nginx y LuaJIT (OpenResty)

Desde hace bastante tiempo, inspirado por un artículo Redimensionar imágenes al vuelo se configuró el redimensionamiento de imágenes usando ngx_http_image_filter_module y todo funcionaba como se esperaba. Pero surgió un problema cuando el gerente necesitaba obtener imágenes con dimensiones exactas para cargar en ciertos servicios, ya que eran sus requisitos técnicos. Por ejemplo, si tenemos un original de imagen de tamaño 1200×1200, y al redimensionar especificamos algo como ?resize=600×400, obtendremos una imagen proporcionalmente reducida por el lado más pequeño, de tamaño 400×400. También es imposible obtener una imagen con una mayor resolución (upscale). Es decir, ?resize=1500×1500 devolverá la misma imagen 1200×1200

Un artículo llegó a ayudar OpenResty: transformamos NGINX en un servidor de aplicaciones completo para entender cómo trabaja Nginx con Lua y la propia biblioteca para Lua isage/lua-imagick — bindings de Lua en C puro para ImageMagick. ¿Por qué se eligió tal solución en lugar de, digamos, algo en Python? Porque es rápido y conveniente. Incluso no necesitarás crear ningún archivo, todo irá directamente en la configuración de Nginx (no es obligatorio).

Así que, ¿qué necesitaremos?

Los ejemplos se darán sobre la base de Debian.

Instalación de nginx y nginx-extras

apt-get update
apt-get install nginx-extras

Instalación de LuaJIT

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

Instalación de imagemagick

apt-get -y install imagemagick

y la biblioteca magickwand asociada, en mi caso para la versión 6

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

Compilación de lua-imagick

Clonamos el repositorio (o simplemente descargamos el zip y lo descomprimimos)

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

Si todo ha ido bien, se puede configurar Nginx.

Aquí un ejemplo de la configuración del backend del host, que de hecho se encarga del redimensionamiento. Se proxiea a través del servidor frontal también con Nginx, donde se realiza la caché por un tiempo (un día) y otras cosas.

configuración de backend 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
    }
}

Lo que se necesitaba (ampliar la imagen por los bordes) se realiza con img:extent() y se determina a través del parámetro resize con un signo + al final.

Los siguientes parámetros están disponibles:

  • WxH (Mantener la relación de aspecto, usar dimensión superior)
  • WxH^ (Mantener la relación de aspecto, usar dimensión inferior (recortar))
  • WxH! (Ignorar la relación de aspecto)
  • WxH+ (Mantener la relación de aspecto, agregar bordes laterales)

Tabla resumen con los resultados del redimensionamiento

Parámetro uri de la solicitud
Tamaño de la imagen salida

?resize=400×200
200×200

?resize=400×200^
400×400

?resize=400×200!
400×200 (No proporcional)

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

Redimensionamiento de imágenes al vuelo con Nginx y LuaJIT (OpenResty)

Summary

Dada toda la potencia y simplicidad de este enfoque, se pueden implementar cosas con una lógica bastante compleja, como agregar marcas de agua o implementar una autorización con control de acceso. Para conocer las capacidades de la API para trabajar con imágenes, se puede consultar la documentación de la biblioteca isage/lua-imagick

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster