До 12.5 млн токенов на тест ИИ-агента
Вход/ Регистрация

Использование Laravel с Timeweb Cloud S3 и Spatie/MediaLibrary

Олег Мещеряков
Олег Мещеряков
Технический писатель
11 декабря 2024 г., обновлено 08 октября 2026
1357
14 минут чтения

Эта статья является продолжением первой части инструкции, где мы подробно рассматривали интеграцию Laravel с S3-хранилищем Timeweb Cloud. В предыдущей части вы узнали, как шаг за шагом настроить подключение к S3 и подготовить приложение для работы с облачным хранилищем.

Теперь мы перейдем к следующему этапу — интеграции пакета Spatie/MediaLibrary, который значительно упрощает управление медиаданными в Laravel. В сочетании с S3 этот инструмент открывает широкие возможности для загрузки, обработки и хранения файлов.

В данной инструкции мы разберем:

  • Установку пакета и зависимостей.
  • Настройку подключения к S3 через конфигурацию Laravel.
  • Настройку моделей для работы с медиаколлекциями и конверсиями изображений.
  • Использование очередей для обработки изображений.
  • Загрузку и управление файлами через маршруты и шаблоны.
  • Отображение медиафайлов с использованием преобразований.
  • Настройку пользовательского домена для S3.
  • Удаление медиафайлов.

Установка

Убедитесь, что установлен GD Graphics Library, необходимый для обработки изображений. Выполните команду:

    
sudo apt-get install php8.x-gd

Замените x на вашу актуальную версию PHP.

Установите библиотеку с помощью Composer:

    
composer require "spatie/laravel-medialibrary:*"

Эта команда устанавливает любую доступную версию пакета spatie/laravel-medialibrary. Символ * означает, что Composer выберет последнюю доступную стабильную версию, которая соответствует остальным ограничениям версий в вашем проекте.

Опубликуйте миграции для библиотеки MediaLibrary:

    
php artisan vendor:publish --provider="Spatie\MediaLibrary\MediaLibraryServiceProvider" --tag="migrations"

Запустите миграции, чтобы создать необходимые таблицы в базе данных:

    
php artisan migrate

Для удобной настройки библиотеки опубликуйте её файл конфигурации:

    
php artisan vendor:publish --provider="Spatie\MediaLibrary\MediaLibraryServiceProvider" --tag="config"

Настройка пакета

Для корректной работы библиотеки Spatie/MediaLibrary настройте подключение S3 и другие параметры.

В файле конфигурации config/media-library.php настройте параметры загрузки. 

Укажите максимальный размер файла и используемый диск. Например:

    
'disks' => [ 'disk_name' => env('MEDIA_DISK', 'public'), 'max_file_size' => 1024 * 1024 * 10, //10Мб ],

В файле .env добавьте переменную для указания используемого диска:

    
MEDIA_DISK=tws3

После этого библиотека будет использовать указанный диск tws3 для хранения медиафайлов.

Настройка модели

Шаг 1: Подключение MediaLibrary к модели

Добавьте в модель app/Models/User.php:

    
use Spatie\MediaLibrary\HasMedia\HasMedia; use Spatie\MediaLibrary\HasMedia\HasMediaTrait; use Spatie\MediaLibrary\Models\Media; use Spatie\Image\Manipulations; class User extends Model implements HasMedia { use HasMediaTrait; }

Шаг 2: Регистрация коллекций и преобразований

Добавьте два метода registerMediaCollections и registerMediaConversions в файле app/Models/User.php:

Метод registerMediaCollections

Этот метод задаёт параметры для хранения медиафайлов, определяя «коллекции». Каждая коллекция описывает, как организовать файлы, связанные с моделью.

    
public function registerMediaCollections(): void { $this ->addMediaCollection('avatars') ->singleFile(); $this ->addMediaCollection('gallery') ->withResponsiveImages(); }
  • addMediaCollection('avatars'): Создаёт коллекцию с именем avatars, которая предназначена для хранения одного изображения (например, аватара).

  • singleFile(): Гарантирует, что в коллекции будет храниться только один файл. Если добавить новый файл, предыдущий будет автоматически удалён.

  • withResponsiveImages(): Автоматически генерирует адаптивные изображения (разные размеры для разных экранов) для всех файлов в этой коллекции. Это полезно для оптимизации отображения изображений на устройствах с разным разрешением.

Метод registerMediaConversions

Этот метод описывает, какие преобразования (конверсии) изображений нужно выполнить для добавленных файлов. Преобразования выполняются автоматически при добавлении файла в коллекцию.

    
public function registerMediaConversions(Media $media = null): void { $this->addMediaConversion('thumb') ->width(150) ->height(150) ->sharpen(10) ->performOnCollections('avatars','gallery'); $this->addMediaConversion('border') ->width(250) ->height(250) ->sharpen(10) ->sepia() ->border(10, 'black', Manipulations::BORDER_OVERLAY) ->performOnCollections('gallery'); }

Подробный разбор метода

addMediaConversion('thumb')

  • Создаёт конверсию с именем thumb (миниатюра).

  • width(150) и height(150): Устанавливают размеры преобразованного изображения — 150x150 пикселей.

  • sharpen(10): Применяет резкость с уровнем 10, чтобы сделать изображение чётче.

  • performOnCollections('avatars', 'gallery'): Указывает, что миниатюра должна создаваться для всех изображений, добавленных в коллекции avatars и gallery.

addMediaConversion('border')

  • Создаёт конверсию с именем border (изображение с рамкой).

  • width(250) и height(250): Устанавливают размеры изображения с рамкой — 250x250 пикселей.

  • sepia(): Накладывает сепию, придавая изображению эффект старины.

  • border(10, 'black', Manipulations::BORDER_OVERLAY): Добавляет чёрную рамку шириной 10 пикселей поверх изображения.

  • performOnCollections('gallery'): Указывает, что это преобразование применяется только к изображениям, добавленным в коллекцию gallery.

Полный листинг файла, который должен получиться:

    
namespace App\Models; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Laravel\Sanctum\HasApiTokens; use Spatie\MediaLibrary\HasMedia; use Spatie\MediaLibrary\InteractsWithMedia; use Spatie\MediaLibrary\MediaCollections\Models\Media; use Spatie\Image\Manipulations; class User extends Authenticatable implements HasMedia { use HasApiTokens, HasFactory, Notifiable, InteractsWithMedia; public function registerMediaCollections(): void { $this ->addMediaCollection('avatars') // Изображение аватара ->singleFile(); $this ->addMediaCollection('gallery') ->withResponsiveImages(); } public function registerMediaConversions(Media $media = null): void { $this->addMediaConversion('thumb') ->width(150) ->height(150) ->sharpen(10) ->performOnCollections('avatars','gallery'); $this->addMediaConversion('border') ->width(250) ->height(250) ->sharpen(10) ->sepia() ->border(10, 'black', Manipulations::BORDER_OVERLAY) ->performOnCollections('gallery'); } protected $fillable = [ 'name', 'email', 'password', 'avatar', ]; protected $hidden = [ 'password', 'remember_token', ]; protected $casts = [ 'email_verified_at' => 'datetime', ]; }

Настройка очередей для обработки изображений

Поскольку процесс нарезки изображений может быть длительным, важно обеспечить бесперебойную работу приложения. Для этого Laravel и библиотека Spatie используют очереди, которые позволяют выполнять задачи в фоновом режиме. В этом примере мы настроим очереди с использованием адаптера database.

Сначала создадим таблицу для хранения задач очереди. Выполните команду:

    
php artisan queue:table

Запустите миграции для создания таблицы в базе данных:

    
php artisan migrate

В файле .env укажите, что для очередей будет использоваться база данных:

    
QUEUE_CONNECTION=database

Чтобы обработать задачи очереди, запустите следующий процесс:

    
php artisan queue:work

Теперь задачи нарезки изображений будут выполняться асинхронно, не блокируя работу приложения.

Загрузка изображений

В файле routes/web.php напишем логику для загрузки изображений:

    
Route::post('/profile/store', function (Request $request) { $user = User::find(1); if ($request->hasFile('avatar')) { $user->addMediaFromRequest('avatar')->toMediaCollection('avatars'); } if ($request->hasFile('gallery')) { $user->addMultipleMediaFromRequest(['gallery'])->each(function ($user) { $user->toMediaCollection('gallery'); }); } return redirect('/profile'); });
  • $user = User::find(1): Получается объект пользователя с ID  из базы данных.

  • if ($request->hasFile(<key>)) {}: Проверяет, был ли передан файл в запросе

  • addMediaFromRequest(<key>): Загружает файл из запроса с указанным ключом

  • toMediaCollection(<key>): Сохраняет файл в медиа-коллекцию. Конверсии изображений (если определены) будут применены автоматически.

  • addMultipleMediaFromRequest(['gallery']): Извлекает файлы из запроса, соответствующие ключу и возвращает коллекцию медиаобъектов для дальнейшей обработки. Метод предназначен для загрузки нескольких файлов за один вызов.

Настройка шаблона

В этом разделе мы настроим шаблон resources/views/profile/create.blade.php для загрузки аватара пользователя и фотогалереи:

    
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Создание пользователя</title> </head> <body> <form action="/profile/store" method="POST" enctype="multipart/form-data"> @csrf <label for="avatar">Выберите изображение:</label> <input type="file" name="avatar" required> <hr> <label for="gallery">Выберите фотографии:</label> <input type="file" name="gallery[]" required multiple> <button type="submit">Загрузить</button> </form> </body> </html>

Инструкция:

  • Откройте в браузере: http://localhost:8000/profile/create.

  • Для загрузки аватара выберите одно изображение в поле avatar.

  • Для загрузки нескольких фотографий в галерею используйте поле gallery[].

  • После выбора файлов нажмите кнопку «Загрузить».

Отображение изображений 

Теперь добавим функционал для отображения загруженных изображений в шаблон resources/views/profile/index.blade.php.

Код отображения аватара

    
@if ($user->hasMedia('avatars')) <img src="{{ $user->getFirstMediaUrl('avatars','thumb') }}" alt="Аватар" style="width: 150px; height: 150px; border-radius: 50%;"> @else <p>Аватар отсутствует.</p> @endif
  • $user->hasMedia('avatars'): Проверяет, есть ли у пользователя медиафайлы в коллекции avatars.

  • $user->getFirstMediaUrl('avatars', 'thumb'): Возвращает URL первого медиафайла в коллекции avatars с преобразованием thumb.

Отображение фотогалереи

Для вывода изображений из коллекции gallery используем следующие варианты:

Вариант 1. С преобразованием thumb:

    
@if ($user->hasMedia('gallery')) @foreach($user->getMedia('gallery') as $media) <img src="{{ $media->getUrl('thumb') }}" alt="Изображение" style="width: 150px; height: 150px;"> @endforeach @else <p>Фотографии отсутствуют.</p> @endif

Вариант 2. С преобразованием border:

    
@if ($user->hasMedia('gallery')) @foreach($user->getMedia('gallery') as $media) <img src="{{ $media->getUrl('border') }}" alt="Изображение" style="width: 150px; height: 150px;"> @endforeach @else <p>Фотографии отсутствуют.</p> @endif

Вариант 3. Вывод всех изображений responsive:

    
@if ($user->hasMedia('gallery')) @foreach($user->getMedia('gallery') as $media) {{ $media }} @endforeach @else <p>Фотографии отсутствуют.</p> @endif

Полный код файла resources/views/profile/index.blade.php:

    
<!doctype html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Профиль пользователя</title> </head> <body> <h1>Профиль пользователя</h1> <p>{{$user->name}}</p> @if ($user->hasMedia('avatars')) <img src="{{ $user->getFirstMediaUrl('avatars','thumb') }}" alt="Аватар" style="width: 150px; height: 150px; border-radius: 50%;"> @else <p>Аватар отсутствует.</p> @endif <h1>Фотографии пользователя с преобразованием thumb</h1> @if ($user->hasMedia('gallery')) @foreach($user->getMedia('gallery') as $media) <img src="{{ $media->getUrl('thumb') }}" alt="Аватар" style="width: 150px; height: 150px;"> @endforeach @else <p>Фотографии отсутствует.</p> @endif <h1>Фотографии пользователя с преобразованием border</h1> @if ($user->hasMedia('gallery')) @foreach($user->getMedia('gallery') as $media) <img src="{{ $media->getUrl('border') }}" alt="Аватар" style="width: 150px; height: 150px;"> @endforeach @else <p>Фотографии отсутствует.</p> @endif <h1>Фотографии пользователя responsive</h1> @if ($user->hasMedia('gallery')) @foreach($user->getMedia('gallery') as $media) {{ $media }} @endforeach @else <p>Фотографии отсутствует.</p> @endif </body> </html>

Для проверки откройте в браузере: http://localhost:8000/profile.

Страница без изображений:

Страница с изображениями:

Настройка пользовательского домена для облачного хранилища 

Для улучшения пользовательского опыта и удобства работы с медиафайлами можно настроить персонализированный домен для обращения к данным в облачном хранилище. Этот процесс включает несколько этапов.

Для примера будет использоваться домен третьего уровня s3.domain.ru.

Шаг 1. Добавьте поддомен в панели управления

2024 12 05 13 20 12

Шаг 2. Настройка DNS-записи

В DNS настройках добавьте CNAME-запись для созданного поддомена, указывающую на адрес s3.twcstorage.ru.

Шаг 3. Привяжите домен к бакету

Свяжите поддомен с вашим бакетом S3 через панель управления Timeweb Cloud.

Подождите 15-20 минут, пока изменения вступят в силу.

Шаг 4. Обновление конфигурации Laravel

В файле .env задайте переменную окружения:

    
TW_URL=https://s3.domain.ru

2024 12 05 13 32 18

Шаг 5. Проверка результата

Откройте приложение в браузере http://localhost:8000/profile и убедитесь, что в HTML-коде атрибут src у медиафайлов содержит ссылку на ваш домен, например:

    
<img src="https://s3.domain.ru/path/to/image.jpg" alt="Медиафайл">

Как удалить изображение

Для удаления изображений добавим функционал в маршруты и шаблон.

Маршрут для удаления

В файле routes/web.php добавьте следующий код:

    
use Spatie\MediaLibrary\MediaCollections\Models\Media; Route::get('mediaDelete/{media}', function (Media $media){ $media->delete(); return back(); })->name('delete.media');

Код в шаблоне

Добавьте код для удаления изображений в файл resources/views/profile/index.blade.php:

    
<h1>Список изображений:</h1> @if ($user->hasMedia('avatars')) <p>Коллекция avatars:</p> @foreach($user->getMedia('avatars') as $media) <a href="{{route('delete.media',$media)}}">Удалить {{$media->file_name}}</a> @endforeach @else <p>Фотографии отсутствует.</p> @endif @if ($user->hasMedia('gallery')) <p>Коллекция gallery:</p> @foreach($user->getMedia('gallery') as $media) <a href="{{route('delete.media',$media)}}">Удалить {{$media->file_name}}</a><br> @endforeach @else <p>Фотографии отсутствует.</p> @endif

Как работает:

  • Откройте в браузере: http://localhost:8000/profile.

  • При клике по ссылке файл удаляется из облачного хранилища.

  • Удаление происходит через маршрут delete.media, который удаляет файл по его идентификатору.

Эта настройка позволяет легко управлять медиафайлами в приложении Laravel.

Выгодные тарифы на облако в Timeweb Cloud

Premium 3.3 ГГц
Санкт-Петербург
12 мес Скидка 10%
Cloud-15

1 242 ₽/мес

Процессор
1 x 3.3 ГГц
Память
1 ГБ
NVMe
15 ГБ
Канал
1 Гбит/с
Публичный IP
Выбор клиентов
Cloud-30

1 926 ₽/мес

Процессор
1 x 3.3 ГГц
Память
2 ГБ
NVMe
30 ГБ
Канал
1 Гбит/с
Публичный IP

Заключение

Использование Spatie/MediaLibrary в сочетании с облачным хранилищем S3 открывает широкие возможности для управления медиафайлами в приложении Laravel. Этот подход предлагает:

  1. Удобство загрузки: Простая настройка форм для загрузки единичных изображений (например, аватаров) и галерей.

  2. Гибкость отображения: Возможность настройки преобразований изображений, таких как создание миниатюр, изображений с рамками или адаптивных версий.

  3. Эффективное хранение: Надежное и масштабируемое хранение данных в S3 с поддержкой пользовательских доменов.

  4. Простое управление: Легкость удаления и замены медиафайлов через маршруты и интерфейс приложения.

  5. Масштабируемость: Обработка изображений в очередях гарантирует стабильность работы даже при больших объемах данных.

Следуя предложенным инструкциям, вы сможете настроить функциональность для загрузки, обработки, отображения и управления изображениями, включая удаление файлов. Эта интеграция значительно повышает удобство работы, производительность и масштабируемость вашего приложения.

11 декабря 2024 г., обновлено 08 октября 2026
1357
14 минут чтения
Средний рейтинг статьи: 5
Предыдущая статья
Следующая статья
Хотите внести свой вклад?
Участвуйте в нашей контент-программе за
вознаграждение или запросите нужную вам инструкцию
img-server
Пока нет комментариев