Загрузка и обработка изображений в Laravel: выбираем движок
Загрузка аватарок и товарных фото упирается в выбор библиотеки обработки, диска хранения и момента ресайза. Разбираем варианты и цену каждого.

Пользователь загружает фото с телефона на 8 мегапикселей и 6 мегабайт, а вы храните его в оригинале и отдаёте в карточку товара размером 300×300. Это лишние мегабайты трафика, медленная выдача и раздувание диска. В Laravel 11.x загрузка файла — три строки, но за ними прячется четыре решения: чем обрабатывать картинку, куда её класть, когда ресайзить и что делать с оригиналом. Ниже — сравнение подходов и критерий, по которому выбрать свой.
Что решаем на самом деле
Сам приём файла в Laravel не проблема. Валидация через image и dimensions, сохранение через store() на нужный диск — это база. Проблема начинается там, где картинку надо привести к нужным размерам, сжать и, возможно, сделать несколько версий (превью, средний, оригинал).
Три развилки, на которых теряют производительность и нервы:
- Движок обработки. GD или Imagick — они дают разное качество, разное потребление памяти и разный набор форматов.
- Библиотека-обёртка. Писать напрямую на GD больно, поэтому берут Intervention Image или spatie/image. Они по-разному устроены.
- Момент обработки. Ресайзить в момент запроса (синхронно) или в очереди (асинхронно). Это влияет на время ответа при загрузке.
Термин: драйвер обработки изображений
Драйвер — это расширение PHP, которое реально гнёт пиксели. GD встроен в большинство сборок PHP и умеет базовое. Imagick — обёртка над ImageMagick, ставится отдельно, ест больше памяти, но лучше работает с большими файлами, CMYK, многослойными форматами и даёт более качественный ресайз.
Движок: GD против Imagick
Это первое, что стоит решить, потому что от него зависит поведение под нагрузкой и доступные форматы.
| Критерий | GD | Imagick |
|---|---|---|
| Установка | Почти всегда уже есть | Ставится отдельно (расширение + ImageMagick) |
| Память на большом файле | Держит всё в памяти как raw-битмап | Тоже прожорлив, но управляем через лимиты ImageMagick |
| Качество ресайза | Приемлемое | Заметно лучше на уменьшении |
| Форматы | JPEG, PNG, GIF, WebP (в свежих сборках), AVIF частично | Шире: TIFF, PDF-превью, CMYK и другое |
| Риски | Может не быть WebP в старой сборке | Исторически были CVE в ImageMagick, нужна policy.xml |
Практический вывод: если вы принимаете обычные фото с телефонов и делаете превью — хватит GD, не усложняйте. Если у вас типография, работа с PDF, CMYK или очень большие исходники — Imagick оправдан, но настройте лимиты и policy.xml, чтобы не словить исчерпание памяти на злонамеренном файле.
Обёртка: Intervention Image против spatie/image
Работать с GD напрямую можно, но код получается многословный. Две популярные библиотеки закрывают это по-разному.
Intervention Image (актуальная 3.x) — низкоуровневый, гибкий API: вы вручную вызываете resize, crop, encode. Требует явно указать драйвер (GD или Imagick) при инициализации. Даёт полный контроль над каждым шагом.
// Intervention Image 3.x, драйвер GD
use Intervention\Image\ImageManager;
use Intervention\Image\Drivers\Gd\Driver;
$manager = new ImageManager(new Driver());
$image = $manager->read($request->file('photo')->getRealPath());
// Вписываем в 800x800, сохраняя пропорции, без апскейла
$image->scaleDown(width: 800, height: 800);
// Кодируем в WebP с качеством 80 и получаем бинарную строку
$encoded = $image->toWebp(quality: 80);
// Кладём на диск через Storage
\Illuminate\Support\Facades\Storage::disk('public')
->put('photos/'.uniqid().'.webp', (string) $encoded);
spatie/image — более высокоуровневый и декларативный, поднимается поверх Intervention. Хорош для типовых цепочек преобразований и читается как список действий. Меньше контроля над byte-уровнем, но код короче.
Отдельно: если задача — версии одного файла (thumbnail, medium, large) и вы уже используете Eloquent-модели, посмотрите на spatie/laravel-medialibrary. Она берёт на себя хранение, привязку к модели, генерацию конверсий и очистку файлов при удалении записи. Это уже не «библиотека ресайза», а полноценная подсистема медиа.
Выбор между «поставить пакет для ресайза» и «поставить media library» — это выбор между функцией и подсистемой. Media library экономит недели, но становится зависимостью, которую придётся тащить и обновлять годами.
Момент обработки: синхронно или в очереди
Вот где чаще всего ошибаются. Если ресайзить прямо в контроллере при загрузке, время ответа складывается из времени сети + времени обработки. На фото 6 МБ и трёх версиях это легко секунды — пользователь смотрит на крутилку, воркер PHP занят.
- Синхронно (в запросе). Проще, результат сразу готов. Оправдано для аватарок и редких загрузок. Опасно, когда версий много или файлы крупные — блокирует PHP-процесс и упирается в
max_execution_timeиmemory_limit. - Асинхронно (очередь). Контроллер быстро сохраняет оригинал и ставит
Jobна обработку. Ответ мгновенный, версии появляются с задержкой. Нужен запущенный воркер (queue:work) и обработка состояния «ещё не готово» в интерфейсе.
Схематично асинхронный путь: контроллер сохраняет исходник на диск, диспатчит job с путём к файлу, job открывает файл, генерирует версии, обновляет запись в БД. Оригинал после генерации версий можно удалить или оставить для повторной генерации при смене требований — это отдельное решение, здесь его не разворачиваем.
Когда что выбирать
| Ситуация | Движок | Обёртка | Момент |
|---|---|---|---|
| Аватарки, одна версия | GD | Intervention или руками | Синхронно |
| Каталог товаров, 3-4 версии | GD | medialibrary | Очередь |
| Крупные исходники, полиграфия | Imagick | Intervention | Очередь |
| Пользовательский контент, высокая нагрузка | GD/Imagick | medialibrary | Очередь + отдельный воркер |
Что ломается на практике
- Память кончается на большом изображении. GD раскладывает картинку в raw-битмап: файл 6 МБ JPEG может занять сотни мегабайт в памяти. Ограничивайте
dimensionsпри валидации, а не только размер файла в мегабайтах. - Загрузили «картинку», а это исполняемый файл. Валидация правилом
imageпроверяет MIME, но не гарантирует безопасность содержимого. Не храните загрузки в publicly-исполняемой директории и не отдавайте их напрямую как PHP. - WebP не поддерживается. На старой сборке GD может не быть WebP. Проверьте
gd_info()или наличие функции кодирования до того, как вкатите это в прод. - EXIF-ориентация. Фото с телефона часто повёрнуто через EXIF-тег. После ресайза ориентация может слететь — многие библиотеки это учитывают, но проверяйте на реальных снимках с iOS и Android.
- Job молча падает. Асинхронная обработка ломается тихо: пользователь видит «нет превью». Настройте
failed_jobsи алерты, иначе узнаете о проблеме от клиента.
Что настроить дальше
После выбора связки: проверьте upload_max_filesize и post_max_size в php.ini — они режут загрузку раньше валидации Laravel. Настройте диск в config/filesystems.php (локальный, S3 или совместимое хранилище) и решите, будете ли вы отдавать картинки через приложение или напрямую с CDN. Для очередей — запустите queue:work под supervisor и следите за глубиной очереди: если она растёт быстрее, чем воркер разгребает, добавляйте воркеры или выносите обработку на отдельный сервер. Мерьте время ответа на эндпоинте загрузки и p95 времени выполнения job — это два показателя, которые скажут, не пора ли переходить с синхронной обработки на очередь.
Генератор изображений: Иллюстрация, обложка, баннер, схема — по текстовому описанию, без исходного фото.
Спросить за 20 ₽Источники: Intervention Image — официальная документация, Laravel Filesystem / File Storage
Частые вопросы
Что выбрать, если я не уверен между GD и Imagick?
У меня Laravel 10, а не 11 — примеры подойдут?
Изображение загружается, но обработка не срабатывает — с чего начать?
Грозит ли безопасности приём файлов от пользователей?
Стоит ли хранить оригинал после генерации версий?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.