Побудова production-ready конвеєра обробки зображень
Зображення — найважча частина будь-якого сайту. Одна необроблена фотографія з камери може важити 10+ МБ і завантажуватися секунди. При цьому користувачі покидають сторінку, якщо LCP перевищує 2.5 секунди. Ми регулярно стикаємося з проєктами, де картинки не оптимізовані: немає ресайзу під різні екрани, немає WebP, водяний знак ставиться вручну. Все це — втрата конверсії та зайві витрати на трафік.
Типовий сценарій: інтернет-магазин з тисячами товарів. Фотографії завантажуються через адмінку, але доводиться робити прев’ю вручну або через костилі. Ми пропонуємо готову інфраструктуру: при завантаженні файл проходить через pipeline, і на виході — одразу кілька варіантів: thumbnail (150x150), medium (800x600), large (1920x1080), всі в WebP та JPEG. Плюс оригінал зберігається окремо.
Водяний знак накладається автоматично, якщо зображення публічне. Ми використовуємо напівпрозорий логотип у правому нижньому куті — це не заважає перегляду, але захищає контент. Всі операції займають не більше 200 мс на одне зображення.
Наш pipeline вирішує ці проблеми автоматично. Ви завантажуєте оригінал — система сама генерує набір прев’ю, конвертує в сучасні формати та накладає watermark. При цьому не потрібно переписувати існуючу логіку: інтеграція займає від 2 до 3 днів.
Які проблеми вирішує конвеєр?
Повільне завантаження через великі оригінали — перша проблема. Одне фото з камери може важити 20 МБ. На мобільному інтернеті це вбиває UX. Ресайз до потрібних розмірів і конвертація в WebP зменшують об’єм у 3-5 разів без втрати якості.
Відсутність адаптивних зображень — друга проблема. Якщо на десктопі показувати картинку 1920x1080, на телефоні вона буде такою ж за розміром, але стиснутою браузером. Це збільшує LCP. Наш pipeline генерує кілька версій під різні роздільні здатності, а ми підключаємо srcset — браузер сам обирає відповідний варіант.
Неможливість автоматично конвертувати формати — третя. Ручна конвертація в WebP або AVIF — довгий процес, який часто забувають. Pipeline робить це на льоту, зберігаючи і оригінал, і похідні. Економія трафіку досягає 40%.
Як ми будуємо pipeline: стек та архітектура
Для синхронної обробки використовуємо Node.js з Sharp. Sharp працює в 3 рази швидше за аналоги на Python. Для асинхронної — Celery + Pillow. Якщо навантаження велике, відправляємо завдання в чергу Redis — сервер не блокується. Альтернатива — imgproxy, який трансформує зображення on-the-fly за URL.
Порівняння підходів:
| Характеристика | Синхронний (Sharp) | Асинхронний (Celery) |
|---|---|---|
| Затримка при завантаженні | 100-300 мс | 0 мс (відповідь одразу) |
| Навантаження на сервер | Високе | Низьке |
| Масштабованість | Обмежена | Висока (черга) |
| Складність | Низька | Середня |
Який обрати? Якщо у вас до 1000 завантажень на день — вистачить синхронного. Для великих проєктів з мільйонами зображень — асинхронний з чергою.
Порівняння форматів:
| Формат | Розмір (відн.) | Якість | Підтримка браузерів |
|---|---|---|---|
| JPEG | 100% | Добра | Всі |
| WebP | 70% | Відмінна | 96% |
| AVIF | 60% | Відмінна | 80% |
Перехід на WebP знижує LCP на 30% та економить до 40% трафіку.
Що входить в роботу?
В послугу входить:
- Проектування архітектури pipeline (вибір підходу, стек).
- Розробка та інтеграція з вашим проєктом (API, middleware).
- Налаштування CDN та кешування (Cloudflare, Vercel).
- Документація з використання та доопрацювання.
- Навчання команди (1 година онлайн).
- Гарантія стабільної роботи — 30 днів підтримки.
Досвід нашої команди — 7+ років у веб-розробці, понад 50 реалізованих проєктів з обробкою зображень.
Процес роботи
- Аналітика — аудит поточної інфраструктури, типові розміри, формати, навантаження.
- Проектування — вибір підходу (синхронний/асинхронний), визначення набору прев’ю.
- Реалізація — написання коду pipeline, інтеграція зі сховищем та CDN.
- Тестування — навантажувальне тестування, перевірка сумісності з браузерами.
- Деплой — розгортання на production, моніторинг.
Терміни та вартість
Базова реалізація з Sharp або imgproxy займає від 2 до 5 днів. Вартість розраховується індивідуально після аудиту — залежить від складності, обсягів та необхідності асинхронної обробки. Оцінимо проєкт безкоштовно протягом дня.
Типові помилки при реалізації
- Ігнорування EXIF-орієнтації — фото з телефону можуть бути повернуті. В Sharp ми автоматично читаємо метадані та коригуємо.
- Втрата прозорості — при конвертації PNG в JPEG фон стає білим. Наш pipeline зберігає альфа-канал або замінює на білий фон.
- Завеликі прев’ю — генерація 10+ варіантів сповільнює обробку. Оптимально 3-4 розміри.
- Відсутність кешування — кожен запит до imgproxy витрачає ресурси. Налаштовуємо Cache-Control на рік.
Приклад коду async pipeline на Celery
# tasks.py (Celery)
from celery import Celery
from PIL import Image
import io, boto3
app = Celery('image_tasks', broker='redis://redis:6379')
@app.task(bind=True, max_retries=3)
def process_image(self, image_id: int):
try:
record = db.get_image(image_id)
raw = s3.get_object(Bucket='uploads', Key=record.original_key)['Body'].read()
img = Image.open(io.BytesIO(raw))
img = ImageOps.exif_transpose(img)
if img.mode == 'RGBA':
background = Image.new('RGB', img.size, (255, 255, 255))
background.paste(img, mask=img.split()[3])
img = background
variants = {}
for name, (w, h) in SIZES.items():
resized = img.copy()
resized.thumbnail((w, h), Image.LANCZOS)
buf = io.BytesIO()
resized.save(buf, 'WEBP', quality=85, method=6)
buf.seek(0)
key = f"processed/{image_id}/{name}.webp"
s3.put_object(Bucket='media', Key=key, Body=buf, ContentType='image/webp', CacheControl='public, max-age=31536000')
variants[name] = key
db.update_image_variants(image_id, variants)
except Exception as exc:
raise self.retry(exc=exc, countdown=60)
Отримайте консультацію по вашому проєкту — оцінимо складність та терміни. Пишіть, ми на зв’язку.







