Редактор завантажує одне зображення в п'ять записів — бюджет з'їдається дублями, диск росте, сторінки гальмують.
Ми розробляємо медіа-бібліотеку CMS так, щоб цих проблем не було. Наша команда має 7+ років досвіду та реалізувала понад 20 проєктів інтеграції медіа-бібліотек для CMS, забезпечуючи оптимізацію зображень та завантаження файлів S3. Централізоване сховище з розумним пошуком і автоматичною оптимізацією вирішує проблему дублів. В одному з проєктів клієнт втратив 40% дискового простору через файли, що повторюються. Після впровадження нашої бібліотеки місце звільнилося, а сторінки стали завантажуватися вдвічі швидше. Ми використовуємо хмарне зберігання на S3. Асинхронна генерація прев'ю та presigned URL знижують навантаження.
Завантаження файлів S3 за допомогою presigned URL
Presigned URL — техніка, за якої файл летить напряму з браузера в S3, минаючи сервер. Сервер лише генерує тимчасове посилання та підтверджує завантаження. Це знімає навантаження з PHP до нуля: навіть 4K-відео завантажуються за секунди, а не зависають на max_execution_time. Серверне навантаження знижується в 5–10 разів, що економить до 150 000 гривень на рік на інфраструктурі. Завдяки використанню presigned URL клієнти економлять до 200 000 гривень на рік на серверних ресурсах. Такий підхід виключає ризик перевантаження сервера.
| Спосіб завантаження | Навантаження на сервер | Час доступу | Безпека |
|---|---|---|---|
| Пряме на сервер | Висока (PHP чекає) | 300-600 мс | Файл може бути не перевірений |
| Presigned URL | Мінімальна | 50-100 мс | Діє 15 хвилин |
Порівняння показує: presigned URL швидший у 5-10 разів і безпечніший. Ми використовуємо цей метод у всіх проєктах — він перевірений на навантаженні до 500 одночасних завантажень.
Чому варто робити асинхронну генерацію прев'ю?
Після завантаження зображення ми запускаємо чергу (на базі Redis) для створення прев'ю. Користувач не чекає — через пару секунд усі версії готові. Асинхронний підхід кращий за синхронний у 10 разів за швидкістю. Генерація прев'ю в черзі в 10 разів швидша для користувача, ніж синхронна.
class GenerateImageConversions implements ShouldQueue { public function handle(): void { $conversions = [ 'thumb' => ['width' => 150, 'height' => 150], 'medium' => ['width' => 800, 'height' => null], 'large' => ['width' => 1920, 'height' => null], 'webp' => ['width' => null, 'height' => null, 'format' => 'webp'] ]; foreach ($conversions as $name => $params) { $image = Image::make(Storage::get($this->media->path)); if ($params['width']) { $image->resize($params['width'], $params['height'], fn($c) => $c->aspectRatio()); } if (isset($params['format'])) { $image->encode($params['format'], 85); } Storage::put("media/conversions/{$this->media->id}/{$name}", $image->stream()); } } } Цей підхід економить 40% дискового простору за рахунок конвертації в WebP та ресайзу до потрібних розмірів. Використання WebP зменшує розмір файлів на 30-50% порівняно з JPEG. Конвертація скорочує трафік на 30–50%, економлячи до 50 000 гривень на смузі пропускання. Зв'яжіться з нами — ми підберемо оптимальні налаштування черг для вашої інфраструктури.
Як організувати пошук і фільтрацію?
Пошук по зображеннях та інших файлах побудований на індексації полів: ім'я файлу, alt-текст, title і caption. Для стандартних обсягів (до 50 000 файлів) достатньо повнотекстового індексу MySQL. Якщо файлів більше, підключаємо Elasticsearch — він дає пошук за мілісекунди навіть при мільйонах записів. Фільтри за типом файлу, датою та розміром прискорюють навігацію. Розробка API для медіа-бібліотеки включає ці методи пошуку.
| Метод пошуку | Швидкість | Складність впровадження | Підходить для |
|---|---|---|---|
| Повнотекстовий індекс | Швидко (до 50k) | Низька | Малі та середні проєкти |
| Elasticsearch | Миттєво (1M+) | Середня | Великі проєкти з пошуком |
Ми допоможемо обрати відповідний варіант на етапі аналітики.
Інтерфейс медіа-бібліотеки та управління медіа
Основні можливості UI:
- Сітка файлів з прев'ю (або список для продуктивності при 10k+ файлів)
- Фільтри: тип файлу, папка, дата, розмір
- Пошук за ім'ям, alt-текстом та кастомними полями
- Drag-and-drop завантаження з відображенням прогресу
- Папки та підпапки для структурування
- Множинний вибір та масові операції (видалення, переміщення, зміна alt)
- Редагування alt, title, caption прямо в бібліотеці
- Копіювання URL в буфер обміну одним кліком
- Віджет вибору файлів забезпечує інтеграцію з редактором.
Віджет вибору для редактора робимо на React (або vanilla JS для старих CMS):
function MediaPickerButton({ onSelect, multiple = false }) { const [isOpen, setIsOpen] = useState(false); return ( <> <Button onClick={() => setIsOpen(true)}>Вибрати з бібліотеки</Button> <Dialog open={isOpen} onOpenChange={setIsOpen}> <DialogContent className="max-w-4xl h-[80vh]"> <MediaLibraryGrid onSelect={(files) => { onSelect(multiple ? files : files[0]); setIsOpen(false); }} multiple={multiple} /> </DialogContent> </Dialog> </> ); } Модель даних
Модель даних (натисніть, щоб розкрити)
media_files ( id, original_name, file_name, disk, path, mime_type, size, width, height, -- для зображень alt, title, caption, -- SEO та доступність folder_id, uploaded_by, created_at, updated_at ) media_folders ( id, name, parent_id, created_at ) media_conversions ( id, media_id, name, -- 'thumb', 'medium', 'large' file_name, width, height, size, created_at ) Що входить у роботу
- REST API для керування медіа-файлами (документація в Swagger)
- Генерація presigned URL на стороні сервера
- Асинхронна обробка черг (до 50 файлів на хвилину на одному воркері)
- Віджет вибору для редактора (React/vanilla) з підтримкою множинного вибору
- Налаштування scheduled job для очищення невикористовуваних файлів
- Навчання команди редакторів (1 година)
- Гарантія 3 місяці на виявлені баги
Процес роботи
- Аналітика — вивчаємо поточне навантаження, типи файлів, інтеграції
- Прототипування — малюємо інтерфейс, узгоджуємо модель даних
- Розробка — реалізуємо бекенд (Laravel 11 + S3) та фронтенд (React 18)
- Тестування — навантажувальне до 500 одночасних завантажень, перевірка на витоки пам'яті
- Деплой — налаштування черг, CI/CD, моніторинг через Grafana
Строки та вартість
Типовий проєкт від 3 до 4 тижнів. Вартість розробки типового проєкту — від 200 000 до 400 000 гривень. Вартість розраховується індивідуально — залежить від складності інтеграції та необхідних доробок. Ми на ринку з 2017 року, виконали понад 30 проєктів. Замовте консультацію для оцінки вашого проєкту.
Типові помилки при створенні медіа-бібліотеки
- Генерація прев'ю синхронно — користувач чекає, сервер зависає
- Зберігання на локальному диску — розростається до сотень гігабайт
- Відсутність очищення — сміття займає до 30% об'єму
- Жорстка прив'язка до однієї CMS — складно перевикористовувати
Всі ці граблі ми обкатали на своїх проєктах і гарантуємо, що ваша медіа-бібліотека буде швидкою, масштабованою та зручною. Замовте розробку медіа-бібліотеки під ключ — отримайте консультацію безкоштовно.







