It has been quite some time since I was inspired by an article that set up image resizing using and everything was working as intended. However, one problem arose when the manager needed to obtain images with exact dimensions for uploads to certain services, as these were their technical requirements. For example, if we have an original image size of 1200×1200, and during the resizing we specify something like ?resize=600×400, we get an image proportionally reduced along the smallest edge to a size of 400×400. It is also impossible to obtain an image with higher resolution (upscale). That is, ?resize=1500×1500 will return the same image 1200×1200
An article came to the rescue to understand how Nginx works with Lua and the Lua library itself — Lua pure-c bindings to ImageMagick. Why this solution was chosen instead of, say, something in Python — because it is fast and convenient. You won't even need to create any files, everything can be done directly in the Nginx configuration (not mandatory).
So, what do we need
Examples will be provided based on Debian.
Installing nginx and nginx-extras
apt-get update
apt-get install nginx-extrasInstalling LuaJIT
apt-get -y install lua5.1 luajit-5.1 libluajit-5.1-devInstalling imagemagick
apt-get -y install imagemagickand the libraries magickwand for it, in my case for version 6
apt-cache search libmagickwand
apt-get -y install libmagickwand-6.q16-3 libmagickwand-6.q16-devBuilding lua-imagick
Clone the repository (or download the zip and unpack it)
cd ~
git clone https://github.com/isage/lua-imagick.git
cd lua-imagick
mkdir build
cd build
cmake ..
make
make installIf everything went successfully, you can configure Nginx.
I will provide an example of the backend host config, which is responsible for resizing. It is proxied by the front server also with Nginx, where caching occurs for a certain amount of time (a day) and other things.
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
}
}
The required task (extending the image at the edges) is done using img:extent() and is defined using the parameter resize with a sign + at the end.
The following parameters are available:
- 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)
Summary table of resizing results
Request uri parameter
Output image size
?resize=400×200
200×200
?resize=400×200^
400×400
?resize=400×200!
400×200 (Not proportional)
?resize=400×200+
400×200 (Proportional)

Summary
Considering the power and simplicity of this approach, you can implement features with quite complex logic, such as adding watermarks or enabling access-controlled authorization. To explore the API capabilities for image processing, you can refer to the library documentation.
Source: habr.com
