nginx als dynamischer Image-Rescaler
NGINX bringt ja einen Adapter zu libgd
mit (ngx_http_image_filter_module), der eine
Skalierung auf kleinere Bildgrößen im Rahmen des Request-/Response-Zyklus ermöglicht.
Leider ohne gutes Caching.
Hier stelle ich eine Lösung vor, die einen Cache (mit nginx-Boardmitteln) für diese Bilder ermöglicht
Image Filter mit fester Skalierung
Standardbeispiel:
location ~ ^/.*\.jpg {
image_filter scale 1000 1000;
}
Diese Konfiguration hat den Nachteil, dass sowohl die Bildgrößen fix sind als auch, dass der Filter bei jedem Request das Bild neu berechnet.
Wichtig: in der nginx.conf
muss vor dem ersten Block (events
oder http
) das Modul geladen werden, falls nicht
bereits statisch gelinkt.
Dies betrifft z.B. das offizielle Docker-Image.
load_module /etc/nginx/modules/ngx_http_image_filter_module.so;
Image Filter mit Skalierung per Parameter
Nächster Schritt: beliebige Skalierungen via Parameter erlauben, mit Fallback auf die Originaldatei:
location / {
## gibt es den Parameter ?w=Zahl
if ($arg_w ~ "\d+") {
## rewrite, so dass das skalierte Bild generiert wird
## geholt wird.
## Annahme: Alle skalierbaren Bilder liegen unter /media
rewrite (?i)^(/media/.*\.jpg)$ /__scaler-cache/$1;
}
## ... normale Angaben der location, wie bisher
}
## Interne URL für die skalierten Bilder
location /__scaler {
internal;
## Prefix wieder entfernen
rewrite ^/__scaler/(.*)$ /$1 break;
add_header X-Image-Scaling "$arg_w x 1280";
## Skalieren mit
image_filter resize $arg_w 1280;
## Maximale Größe des Originalbildes
image_filter_buffer 50M;
}
Ein Bild, das via image.jpg?w=1280
geladen wird, wird nun automatisch auf eine maximale Höhe und Breite von jeweils
1280 Pixel skaliert.
Leider noch ohne Caching.
Image Filter mit Skalierung per Parameter und Caching
Nginx unterstützt über ngx_http_proxy_module ein sehr gut einstellbaren Cache für die Verwendung als reverse-proxy. Zusammen mit den vorherigen Parts ergibt sich nun folgende Konfiguration:
proxy_cache_path /tmp/cache levels=1:2 keys_zone=img_cache:10m max_size=100m
inactive=30m use_temp_path=off;
#...
server {
#...
## Cache für die skalierten Images
location /__scaler-cache {
internal;
expires 6h;
proxy_cache_methods GET HEAD;
## Erzwungene Reloads sollen kein Cache nutzen
proxy_cache_bypass $cookie_nocache $arg_nocache $http_pragma;
## Name des caches
proxy_cache img_cache;
## MISS/HIT/BYPASS Information
add_header X-Image-Cache $upstream_cache_status;
proxy_cache_valid 200 6h;
proxy_cache_revalidate on;
## Weiterleitung zu der nachfolgenden `__scaler` konfiguration.
proxy_pass http://127.0.0.1:80/__scaler;
}
location /__scaler {
## wird nur von __scaler_cache aufgerufen
allow 127.0.0.1/32;
deny all;
## Prefix wieder entfernen
rewrite ^/__scaler/(.*)$ /$1 break;
add_header X-Image-Scaling "$arg_w x 1280";
## Skalieren mit
image_filter resize $arg_w 1280;
## Maximale Größe des Originalbildes
image_filter_buffer 50M;
}
location / {
if ($arg_w ~ "\d+") {
rewrite (?i)^(/media/.*\.jpg)$ /__scaler-cache/$1;
}
## ...
}
## ...
}
Mit ein paar map
s und if
s lässt sich das auch noch auf einstellbare Breite und Höhe erweitern:
# Default für Breite und Höhe
map $arg_w $width {
"" "-";
default "$arg_w";
}
map $arg_h $height {
"" "-";
default "$arg_h";
}
server {
#... wie oben
location /__scaler {
## wird nur von __scaler_cache aufgerufen
allow 127.0.0.1/32;
deny all;
## Prefix wieder entfernen
rewrite ^/__scaler/(.*)$ /$1 break;
add_header X-Image-Scaling "$width x $height";
## Skalieren auf die angegebene maximale Größe bei Beibehaltung des Seitenverhältnis
image_filter resize $width $height;
## Maximale Größe des Originalbildes
image_filter_buffer 50M;
}
location / {
set $do_scale 0;
if ($arg_w ~ "\d+") {
set $do_scale 1;
}
if ($arg_h ~ "\d+") {
set $do_scale 1;
}
if ($do_scale = 1) {
rewrite (?i)^(/media/.*\.(jpg|png))$ /__scaler-cache/$1;
}
## ...
}
##...
}