<div><img src="https://top-fwz1.mail.ru/counter?id=3548135;js=na" style="position:absolute;left:-9999px;" alt="Top.Mail.Ru" /></div>
Бесплатный перенос IT-инфраструктуры в облако

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

Олег Мещеряков
Олег Мещеряков
Технический писатель
11 декабря 2024 г.
37
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"
cloud

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

Для корректной работы библиотеки 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.timeweb.cloud.

Шаг 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

Заключение

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

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

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

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

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

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

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

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