3.5. Загрузка файлов

3.5. Загрузка файлов

Вы предпочаете изучать визуально?
Освойте Livewire с помощью наших подробных видеокурсов
Смотрите сейчас

Livewire предоставляет мощную поддержку загрузки файлов внутри ваших компонентов.

Сначала добавьте трейд WithFileUploads в ваш компонент. После того как этот трейд будет добавлен в компонент, вы сможете использовать wire:model для полей ввода файлов, как для любых других типов полей ввода, и Livewire позаботится обо всем остальном.

Вот пример простого компонента, который обрабатывает загрузку фотографии:

<?php
 
namespace App\Livewire;
 
use Livewire\Component;
use Livewire\WithFileUploads;
use Livewire\Attributes\Validate;
 
class UploadPhoto extends Component
{
use WithFileUploads;
 
#[Validate('image|max:1024')] // 1MB Max
public $photo;
 
public function save()
{
$this->photo->store(path: 'photos');
}
}
<form wire:submit="save">
<input type="file" wire:model="photo">
 
@error('photo') <span class="error">{{ $message }}</span> @enderror
 
<button type="submit">Save photo</button>
</form>
Метод "upload" зарезервирован

Обратите внимание, что в приведенном выше примере используется метод "save" вместо метода "upload". Это распространенная ошибка. Термин "upload" зарезервирован в Livewire. Вы не можете использовать его в качестве имени метода или свойства в вашем компоненте.

С точки зрения разработчика, обработка полей ввода файлов ничем не отличается от обработки любых других типов полей ввода: добавьте wire:model в тег <input>, и все остальное будет обработано автоматически.

Однако за кулисами происходит большее, чтобы загрузка файлов работала в Livewire. Вот краткий обзор того, что происходит, когда пользователь выбирает файл для загрузки:

  1. Когда выбран новый файл, JavaScript Livewire отправляет начальный запрос к компоненту на сервере, чтобы получить временный "подписанный" URL для загрузки.
  2. После получения URL JavaScript выполняет собственно "загрузку" на подписанный URL, сохраняет файл в временной директории, указанной Livewire, и возвращает уникальный хеш ID загруженного файла.
  3. После того как файл загружен и уникальный хеш ID сгенерирован, JavaScript Livewire отправляет финальный запрос к компоненту на сервере, чтобы "установить" нужное публичное свойство на новый временный файл.
  4. Теперь публичное свойство (в данном случае $photo) установлено на временный файл и готово к сохранению или валидации в любой момент.

1. Хранение загруженных файлов

Предыдущий пример демонстрирует самый базовый сценарий хранения: перемещение временно загруженного файла в директорию "photos" на стандартном диске файловой системы приложения.

Однако, возможно, вы захотите настроить имя файла для сохраненного файла или даже указать конкретный "диск" хранения, на котором будет храниться файл (например, S3).

Оригинальные имена файлов

Вы можете получить оригинальное имя файла временной загрузки, вызвав метод ->getClientOriginalName().

Livewire использует те же API, что и Laravel для хранения загруженных файлов, поэтому не стесняйтесь обращаться к документации по загрузке файлов в Laravel. Однако ниже приведены несколько распространенных сценариев хранения и примеров:

public function save()
{
// Store the file in the "photos" directory of the default filesystem disk
$this->photo->store(path: 'photos');
 
// Store the file in the "photos" directory in a configured "s3" disk
$this->photo->store(path: 'photos', 's3');
 
// Store the file in the "photos" directory with the filename "avatar.png"
$this->photo->storeAs(path: 'photos', name: 'avatar');
 
// Store the file in the "photos" directory in a configured "s3" disk with the filename "avatar.png"
$this->photo->storeAs(path: 'photos', name: 'avatar', 's3');
 
// Store the file in the "photos" directory, with "public" visibility in a configured "s3" disk
$this->photo->storePublicly(path: 'photos', 's3');
 
// Store the file in the "photos" directory, with the name "avatar.png", with "public" visibility in a configured "s3" disk
$this->photo->storePubliclyAs(path: 'photos', name: 'avatar', 's3');
}

2. Обработка нескольких файлов

Livewire автоматически обрабатывает загрузку нескольких файлов, распознавая атрибут multiple в теге <input>.

Например, ниже приведен компонент с массивом свойств, названным $photos. Добавив атрибут multiple к полю ввода файлов формы, Livewire автоматически добавит новые файлы в этот массив:

use Livewire\Component;
use Livewire\WithFileUploads;
use Livewire\Attributes\Validate;
 
class UploadPhotos extends Component
{
use WithFileUploads;
 
#[Validate(['photos.*' => 'image|max:1024'])]
public $photos = [];
 
public function save()
{
foreach ($this->photos as $photo) {
$photo->store(path: 'photos');
}
}
}
<form wire:submit="save">
<input type="file" wire:model="photos" multiple>
 
@error('photos.*') <span class="error">{{ $message }}</span> @enderror
 
<button type="submit">Save photo</button>
</form>

3. Валидация файлов

Как мы уже обсуждали, валидация загрузки файлов с помощью Livewire такая же, как и обработка загрузки файлов в стандартном контроллере Laravel.

Убедитесь, что S3 правильно настроен

Многие правила валидации, связанные с файлами, требуют доступа к файлу. При загрузке непосредственно в S3 эти правила валидации не сработают, если объект файла в S3 не доступен публично.

Для получения дополнительной информации о валидации файлов, обратитесь к документации по валидации файлов в Laravel.

4. Временные URL-адреса для предварительного просмотра

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

Livewire упрощает это с помощью метода ->temporaryUrl() для загруженных файлов.

Временные URL-адреса ограничены изображениями

По соображениям безопасности временные URL-адреса для предварительного просмотра поддерживаются только для файлов с MIME-типами изображений.

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

use Livewire\Component;
use Livewire\WithFileUploads;
use Livewire\Attributes\Validate;
 
class UploadPhoto extends Component
{
use WithFileUploads;
 
#[Validate('image|max:1024')]
public $photo;
 
// ...
}
<form wire:submit="save">
@if ($photo)
<img src="{{ $photo->temporaryUrl() }}">
@endif
 
<input type="file" wire:model="photo">
 
@error('photo') <span class="error">{{ $message }}</span> @enderror
 
<button type="submit">Save photo</button>
</form>

Как уже обсуждалось, Livewire сохраняет временные файлы в непубличной директории, поэтому обычно нет простого способа предоставить временный публичный URL для предварительного просмотра изображения вашим пользователям.

Однако Livewire решает эту проблему, предоставляя временный подписанный URL, который имитирует загруженное изображение, чтобы ваша страница могла показывать предварительный просмотр изображения пользователям.

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

Временные подписанные URL-адреса для S3

Если вы настроили Livewire для использования S3 для временного хранения файлов, вызов ->temporaryUrl() сгенерирует временный подписанный URL, который будет указывать непосредственно на S3, чтобы предварительные просмотры изображений не загружались с вашего сервера приложения Laravel.

5. Тестирование загрузки файлов

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

Ниже приведен полный пример тестирования компонента UploadPhoto с использованием Livewire:

<?php
 
namespace Tests\Feature\Livewire;
 
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use App\Livewire\UploadPhoto;
use Livewire\Livewire;
use Tests\TestCase;
 
class UploadPhotoTest extends TestCase
{
/** @test */
public function can_upload_photo()
{
Storage::fake('avatars');
 
$file = UploadedFile::fake()->image('avatar.png');
 
Livewire::test(UploadPhoto::class)
->set('photo', $file)
->call('upload', 'uploaded-avatar.png');
 
Storage::disk('avatars')->assertExists('uploaded-avatar.png');
}
}

Ниже приведен пример компонента UploadPhoto, необходимого для успешного прохождения предыдущего теста:

use Livewire\Component;
use Livewire\WithFileUploads;
 
class UploadPhoto extends Component
{
use WithFileUploads;
 
public $photo;
 
public function upload($name)
{
$this->photo->storeAs('/', $name, disk: 'avatars');
}
 
// ...
}

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