Редактор загружает одно изображение в пять записей — бюджет съедается дублями, диск растёт, страницы тормозят.
Мы разрабатываем медиа-библиотеку для CMS так, чтобы этих проблем не было. Централизованное хранилище с умным поиском и автоматической оптимизацией решает проблему дублей. В одном из проектов клиент потерял 40% дискового пространства из-за повторяющихся файлов. После внедрения нашей библиотеки место освободилось, а страницы стали грузиться вдвое быстрее. Мы используем облачное хранение на S3. Асинхронная генерация превью и presigned URL снижают нагрузку.
Как ускорить загрузку изображений?
Presigned URL — техника, при которой файл летит напрямую с браузера в S3, минуя сервер. Сервер только генерирует временную ссылку и подтверждает загрузку. Это снимает нагрузку с PHP до нуля: даже 4K-видео загружаются за секунды, а не зависают на max_execution_time. Серверная нагрузка снижается в 5–10 раз, что экономит до 150 000 рублей в год на инфраструктуре. Такой подход исключает риск перегрузки сервера.
| Способ загрузки | Нагрузка на сервер | Время доступа | Безопасность |
|---|---|---|---|
| Прямая на сервер | Высокая (PHP ждёт) | 300-600 мс | Файл может быть не проверен |
| Presigned URL | Минимальная | 50-100 мс | Действует 15 минут |
Сравнение показывает: presigned URL быстрее в 5-10 раз и безопаснее. Мы используем этот метод во всех проектах — он проверен на нагрузке до 500 одновременных загрузок.
Почему стоит делать асинхронную генерацию превью?
После загрузки изображения мы запускаем очередь (на базе Redis) для создания превью. Пользователь не ждёт — через пару секунд все версии готовы.
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 и ресайза до нужных размеров. Конвертация сокращает трафик на 30–50%, экономя до 50 000 рублей на полосе пропускания. Свяжитесь с нами — мы подберём оптимальные настройки очередей для вашей инфраструктуры.
Как организовать поиск и фильтрацию?
Поиск в медиа-библиотеке построен на индексации полей: имя файла, alt-текст, title и caption. Для стандартных объёмов (до 50 000 файлов) достаточно полнотекстового индекса MySQL. Если файлов больше, подключаем Elasticsearch — он даёт поиск за миллисекунды даже при миллионах записей. Фильтры по типу файла, дате и размеру ускоряют навигацию.
| Метод поиска | Скорость | Сложность внедрения | Подходит для |
|---|---|---|---|
| Полнотекстовый индекс | Быстро (до 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 недель. Стоимость рассчитывается индивидуально — зависит от сложности интеграции и необходимых доработок. Закажите консультацию для оценки вашего проекта.
Типичные ошибки при создании медиа-библиотеки
- Генерация превью синхронно — пользователь ждёт, сервер зависает
- Хранение на локальном диске — разрастается до сотен гигабайт
- Отсутствие очистки — мусор занимает до 30% объёма
- Жёсткая привязка к одной CMS — сложно переиспользовать
Все эти грабли мы обкатали на своих проектах и гарантируем, что ваша медиа-библиотека будет быстрой, масштабируемой и удобной. Закажите разработку медиа-библиотеки под ключ — получите консультацию бесплатно.







