Истории успеха наших клиентов — лучшие проекты
Вход/ Регистрация

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

758
14 минут чтения
Средний рейтинг статьи: 5

Эта статья является продолжением первой части инструкции, где мы подробно рассматривали интеграцию 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

Cloud MSK 15

477 ₽/мес

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

657 ₽/мес

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

Заключение

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

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

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

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

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

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

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

758
14 минут чтения
Средний рейтинг статьи: 5
Хотите внести свой вклад?
Участвуйте в нашей контент-программе за
вознаграждение или запросите нужную вам инструкцию
img-server
Пока нет комментариев