Ресайзинг картинок в nginx
Зачастую на мультимедийных сайтах существует задача выдавать картинки (часто пользовательское фото) в различных размерах (thumbnails). Причем, в большинстве случаев приходится поддерживать несколько размерных версий картинок (например, пользовательское фото vkontakte.ru имеет 3 различные размерные версии).
Один из примеров построения системы отдачи изображений описан в статье “Отдача и ресайзинг фотографий“. Тем не менее, существует несколько альтернативных вариантов. Подробнее об одном из них в этой статье.
Создатель одного из самых популярных в мире серверов для высоконагруженых проектов (речь идет о nginx) порадовал новым модулем “ngx_http_image_filter_module”. Модуль позволяет решать задачу ресайзинга картинок напрямую через Web сервер (без дополнительных звеньев). Как все это выглядит, и что для этого нужно сделать?
Ресайзинг с помощью Web сервера nginx
Как все работает показано ниже:

Как видно из рисунка, nginx уже содержит в себе все, что необходимо для ресайзинга и не требует никаких дополнительных технологий.
Установка
Внимание! Для устновки модуля “ngx_http_image_filter_module” Вам понадобится самая последняя версия nginx (сейчас 0.7.55).
Качаем самую последнюю версию тут.
Для начала, Вам понадобиться установить libgd:
sudo apt-get install libgd2-xpm-dev
По умолчанию, необходимый нам модуль не собирается, поэтому его нужно подключить на этапе конфигурации установки nginx:
./configure --with-http_image_filter_module make sudo make install
Конфигурация сервера
Теперь конфигурируем virtual host в nginx для обработки картинок. Пусть изображения хранятся в директории “/home/test/photos” и мы хотим показывать две размерных версии + оригинал. Правила такие:
- При запросе вида “example.com/a/<имя-картинки>” будем показывать версию 75х75, обрезанную по краям (т.е. всегда квадратную)
- “example.com/b/<имя-картинки>” - показываем версию, вписанную в квадрат 200×200
- “example.com/<имя-картинки>” - показываем оригинал изображения
Собственно наша конфигурация:
# Resizing server
server {
listen 8081;
server_name localhost;
# 'a' size, 75x75 with crop
location /a/ {
proxy_pass http://imageserver; # Backend image server
image_filter crop 75 75; # Resize photo 75x75 and crop
error_page 415 = /empty; # Handle error by /empty location
}
# 'b' size 200x200
location /b/ {
proxy_pass http://imageserver;
image_filter resize 200 200; # Resize photo 200x200
error_page 415 = /empty;
}
# Original image
location / {
proxy_pass http://imageserver; # Proxiing with no filtering
}
# Error handler
location = /empty {
empty_gif; # Respond with empty image
}
}
# Backend image server
server {
listen 8082;
server_name localhost;
root /home/test/photos;
rewrite ^/[ab]/(.*)$ /$1 last;
}
# Upstream
upstream imageserver {
server localhost:8082;
}
Теперь, если у нас в папке “/home/test/photos” есть картинка с названием test.jpg, то протестировать работу сервера можно следующим образом:
- localhost:8081/test.jpg - покажет оригинальное изображение
- localhost:8081/a/test.jpg - покажет версию 75х75
- localhost:8081/b/test.jpg - покажет версию 200х200
Мы подняли сервер на порту 8081, который будет отдавать различные версии картинок. Для каждой версии - отдельная директива location. Что нас интересует - это директива image_filter. Мы использовали ее в двух вариантах:
- image_filter resize A B - уменьшает картинку пропорционально для вписывания в заданые размеры АхВ
- image_filter crop A B - уменьшает картинку и обрезает по краям большую е сторону таким образом, что-бы конечный размер точно соответствовал АхВ
Сервер, слушающий порт 8082, выдает изображения из папки “/home/test/photos“, причем переписывает путь, если есть префикс размера (/a/ или /b/) с помощью директивы rewrite. Для того, что-бы организовать бекенд сервер с порта 8082 мы использовали upstream.
В случае ошибки ресайзинга, модуль отдает ощибку 415, которую можно обработать. В нашем примере, в случае такой ошибки мы показываем пустой gif.
Модуль ресайзинга позволяет работать с форматами JPEG, GIF и PNG.
Документация по модулю ngx_http_image_filter_module


Стоить отметить, что этот модуль предназначен для тех интернет-ресурсов, где присутствует большой поток изображений. Это, например, фотохостинги, большие социальные сети, большие новостные порталы — в которых процент показываемых изображений относительно общего количества достаточно невысок.
Если всё таки было принято решение использовать именно этот подход, стоить учитывать, что это операция дорогостоящая, применительно к процесорным ресурсам. Рекомендуется выносить эту работу с фронтэнда и выделять отдельные сервера, которые будут заниматься исключительно переобжимкой.
Также следует помнить, что на время переобжимки блокируется nginx-овский воркер, количество которых рекомендуется устанавливать равным количеству ядер в системе.
Очень стоящее замечание, спасибо!
Могу дополнить следующее. Для избежания постоянного ресайзинга одних и тех же фотографий, можно прибегнуть к кешированию либо средствами того же nginx либо доп. кеширующим сервером.
Вроде бы Игорь обещал в следующих релизах реализовать всякие _cache_ директивы. Будем ждать
@Den Golotyuk
Наскільки я зрозумів з обох статтей про ресайзинг картинок - ресайз відбувається в момент запиту картинки юзером? Правильно?
@VojToshik
Правильно!
@Den Golotyuk
А чому б не використовувати стандартний метод створення самбнейла і збереження його на вінчестері.
Плюси:
Не потрібно кожен раз витрачати ресурси на ресайз (як ти сам згадував в кількох статтях - ресайзинг процес досить прожорливий) => Швидкість віддачі юзеру суттєво зростає
Мінуси:
-Збільшення об”єму на вічестері (думаю, що цей мінус “мелочный”)
-Виникають певні проблеми, якщо потрібно буде у майбутньому змінити розмір самбнейлів (це досить просто вирішується. головне зберігати оригінал картинки або картинку близьку до оригіналу)
Якщо згоден з представленим методом, тоді скажи, чому краще робити пережимку картино “на льоту”
@VojToshik
На самом деле эта статья описывает только возможности nginx’a по ресайзу картинок. Это не пример реальной среды! В этой статье: http://highload.com.ua/index.php/2009/04/22/отдача-и-ресайзинг-фотографий/ хорошо описан именно реальный пример. Обратите внивание, что там фигурирует кеш-сервер.
Суть в том, что оптимальное решение является сурогатным. Картинка ресайзится на лету, но только один раз, после чего полученное изображение сохраняется на диск (или в память). Второй запрос к ней вернет картинку из кеша. Это решение максимально эффективно, т.к.:
1. Позволяет сохранить гибкость для быстрого изменения/добавления новых размеров
2. Позволяет разбалансировать сам процесс ресайзинга, т.к. он выполняется только по требованию - наиболее важный фактор
3. Само решение очень простое - даже проще, чем создание всех типов-размеров сразу после загрузки картинки
@Den Golotyuk
А, тепер зрозуміло.
з.і. Можеш до мене і на ти. Колись все-таки працювали разом =)
@VojToshik
ok