Загрузка файлов⚓︎
Livewire предоставляет мощную поддержку загрузки файлов прямо внутри ваших компонентов.
Сначала добавьте трейт WithFileUploads в ваш компонент. После того как этот трейт добавлен в компонент, вы можете использовать wire:model на полях ввода типа file точно так же, как и с любыми другими типами полей, а Livewire позаботится обо всём остальном.
Вот пример простого компонента, который обрабатывает загрузку фотографии:
<?php
use Livewire\Attributes\Validate;
use Livewire\WithFileUploads;
use Livewire\Component;
new class extends Component {
use WithFileUploads;
#[Validate('image|max:1024')] // максимум 1MB
public $photo;
public function save()
{
$this->validate();
$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">Сохранить фото</button>
</form>
Метод upload зарезервирован
Обратите внимание, что в примере выше используется метод save вместо upload. Это распространённая ловушка. Слово upload зарезервировано Livewire. Вы не можете использовать его как имя метода или свойства в вашем компоненте.
С точки зрения разработчика, работа с полями ввода файлов ничем не отличается от работы с любыми другими типами полей: добавляете wire:model к тегу <input> — и всё остальное Livewire делает за вас.
Однако под капотом происходит гораздо больше, чтобы загрузка файлов в Livewire работала. Вот краткий обзор того, что происходит, когда пользователь выбирает файл для загрузки:
- Когда выбирается новый файл, JavaScript Livewire отправляет начальный запрос к компоненту на сервере, чтобы получить временный «подписанный» URL для загрузки.
- Получив URL, JavaScript выполняет саму загрузку файла по этому подписанному URL, сохраняя файл во временную директорию, определённую Livewire, и возвращает уникальный хеш-ID нового временного файла.
- После успешной загрузки файла и получения уникального хеш-ID, JavaScript Livewire делает финальный запрос к компоненту на сервере, сообщая ему, что нужно «установить» указанное публичное свойство в новый временный файл.
- Теперь публичное свойство (в данном случае
$photo) содержит временно загруженный файл и готово к сохранению или валидации в любой момент.
Сохранение загруженных файлов⚓︎
Предыдущий пример демонстрирует самый простой сценарий сохранения: перемещение временно загруженного файла в директорию «photos» на диске файловой системы приложения по умолчанию.
Однако вы можете захотеть настроить имя сохраняемого файла или указать конкретный диск хранения (например, S3).
Оригинальные имена файлов
Вы можете получить оригинальное имя загруженного файла, вызвав метод ->getClientOriginalName() у временной загрузки.
Livewire использует те же API, что и Laravel для сохранения загруженных файлов, поэтому смело обращайтесь к документации Laravel по загрузке файлов. Ниже приведены несколько распространённых сценариев сохранения и примеры:
<?php
public function save()
{
// Сохраняем файл в директорию "photos" диска файловой системы по умолчанию
$this->photo->store(path: 'photos');
// Сохраняем файл в директорию "photos" на настроенном диске "s3"
$this->photo->store(path: 'photos', options: 's3');
// Сохраняем файл в директорию "photos" с именем файла "avatar.png"
$this->photo->storeAs(path: 'photos', name: 'avatar');
// Сохраняем файл в директорию "photos" на настроенном диске "s3" с именем файла "avatar.png"
$this->photo->storeAs(path: 'photos', name: 'avatar', options: 's3');
// Сохраняем файл в директорию "photos" с публичной видимостью ("public") на настроенном диске "s3"
$this->photo->storePublicly(path: 'photos', options: 's3');
// Сохраняем файл в директорию "photos" с именем "avatar.png" и публичной видимостью ("public") на настроенном диске "s3"
$this->photo->storePubliclyAs(path: 'photos', name: 'avatar', options: 's3');
}
Если вы храните файлы в S3 — или в совместимом с S3 сервисе, таком как Cloudflare R2 или DigitalOcean Spaces, — раздел Использование S3 ниже подробно описывает полную настройку с нуля.
Обработка нескольких файлов⚓︎
Livewire автоматически обрабатывает загрузку нескольких файлов, определяя наличие атрибута multiple у тега <input>.
Например, ниже приведён компонент со свойством-массивом под названием $photos. Добавив атрибут multiple к полю ввода файла в форме, Livewire будет автоматически добавлять новые файлы в этот массив:
<?php
use Livewire\Attributes\Validate;
use Livewire\WithFileUploads;
use Livewire\Component;
new class extends Component {
use WithFileUploads;
#[Validate(['photos.*' => 'image|max:1024'])]
public $photos = [];
public function save()
{
$this->validate();
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">Сохранить фото</button>
</form>
Если у поля ввода файлов, привязанного к свойству-массиву, отсутствует атрибут multiple, Livewire всё равно рассматривает каждую загрузку как множественную и добавляет файл в массив — структура свойства имеет приоритет. Задача атрибута multiple — сообщить браузеру, что разрешён выбор нескольких файлов одновременно.
Валидация файлов⚓︎
Как мы уже обсуждали, валидация загруженных файлов в Livewire выполняется точно так же, как обработка загрузки файлов в обычном контроллере Laravel.
В качестве удобства Livewire выполняет быструю предварительную проверку, когда это возможно: правила размера (max, min, size, between) и правила типа (image, mimes, mimetypes, extensions), объявленные для свойства, проверяются по метаданным выбранного файла до начала загрузки. Поэтому при выборе видео размером 200 МБ с правилом image|max:1024 ошибка валидации будет показана мгновенно, а не после длительной загрузки. Такая предварительная проверка отклоняет только те файлы, чьё указанное имя или тип явно нарушают правило — окончательная проверка всегда выполняется на сервере для реального файла после загрузки.
Убедитесь, что S3 правильно настроен
Многие правила валидации файлов требуют доступа к самому файлу. При хранении временных загрузок напрямую в S3 эти правила валидации завершатся ошибкой, если объект файла в S3 недоступен публично.
Подробную информацию о валидации файлов смотрите в документации Laravel по правилам валидации файлов.
Временные URL для предпросмотра⚓︎
После того как пользователь выбрал файл, обычно следует показать ему предпросмотр этого файла ещё до отправки формы и сохранения файла.
Livewire делает это очень просто с помощью метода ->temporaryUrl() у загруженных файлов.
Временные URL поддерживаются только для изображений
По соображениям безопасности временные URL предпросмотра работают только для файлов с MIME-типами изображений.
Давайте рассмотрим пример загрузки файла с предпросмотром изображения:
<?php
use Livewire\Attributes\Validate;
use Livewire\WithFileUploads;
use Livewire\Component;
new class 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">Сохранить фото</button>
</form>
Как уже обсуждалось ранее, Livewire сохраняет временные файлы в непубличной директории, поэтому обычно нет простого способа предоставить пользователям временный публичный URL для предпросмотра изображений.
Однако Livewire решает эту проблему, предоставляя временный подписанный URL, который «притворяется» загруженным изображением, чтобы ваша страница могла показать предпросмотр изображения пользователям.
Этот URL защищён от показа файлов из директорий, находящихся выше временной директории. А поскольку он подписан, пользователи не могут злоупотреблять этим URL, чтобы просматривать другие файлы в вашей системе.
Временные подписанные URL для S3
Если вы настроили Livewire на использование S3 для хранения временных файлов, вызов метода ->temporaryUrl() будет генерировать временный подписанный URL напрямую к S3, так что предпросмотр изображений не будет загружаться через сервер вашего Laravel-приложения.
Полноценные объекты загрузки в JavaScript⚓︎
Свойства файлов полезны не только на сервере — на клиентской стороне $wire.photo является полноценным объектом загрузки, а не непрозрачной строкой. Это позволяет создавать мгновенные полностью клиентские предпросмотры и получать состояние загрузки без ожидания обращения к серверу:
<form wire:submit="save">
<div x-show="$wire.photo">
<img x-bind:src="$wire.photo?.previewUrl">
<progress max="100" x-bind:value="$wire.photo?.progress" x-show="$wire.photo?.isUploading"></progress>
<button type="button" x-on:click="$wire.photo.remove()">Удалить</button>
</div>
<input type="file" wire:model="photo">
<button type="submit">Сохранить фотографию</button>
</form>
В момент, когда пользователь выбирает файл, свойство оптимистично получает объект ожидающей загрузки — ещё до того, как какие-либо байты попадут на сервер. Его previewUrl — это локальный blob URL, созданный из файла, который уже находится в браузере, поэтому предпросмотр появляется мгновенно и файл не загружается повторно. Состояние progress обновляется реактивно по мере выполнения загрузки.
Объект загрузки предоставляет:
| Свойство | Описание |
|---|---|
name |
Исходное имя файла с устройства пользователя |
extension |
Расширение файла, полученное из name |
kind |
Общая категория файла: 'image', 'audio', 'video' или 'file' — определяется по MIME-типу браузера во время загрузки, а после загрузки — по расширению имени файла |
isImage / isAudio / isVideo |
Краткие проверки на основе kind, например wire:show="photo.isImage" |
filename |
Хешированное временное имя файла на сервере (null во время загрузки) |
isUploading |
true, пока загрузка ещё выполняется |
progress |
Прогресс загрузки от 0 до 100 (после завершения остаётся равным 100) |
isPreviewable |
Доступен ли URL для предпросмотра |
previewUrl |
Лучший доступный URL для предпросмотра: локальный blob URL, когда это возможно, иначе подписанный URL сервера |
temporaryUrl() |
Подписанный URL предпросмотра на стороне сервера (JavaScript-аналог PHP-метода ->temporaryUrl()) |
remove() |
Мгновенно удаляет эту загрузку из свойства — свойство обновляется оптимистично, а сервер подтверждает изменение в фоновом режиме (текущие загрузки отменяются вместо этого) |
Свойства, содержащие несколько загрузок, гидратируются в массивы полноценных объектов, поэтому каждый файл можно отдельно отображать и удалять:
<div>
<input type="file" wire:model="photos" multiple>
<template x-for="photo in $wire.photos" :key="photo.name">
<div>
<img x-bind:src="photo.previewUrl">
<span x-text="photo.name"></span>
<button type="button" x-on:click="photo.remove()">Удалить</button>
</div>
</template>
</div>
Blob URL и политика безопасности содержимого
Локальные предпросмотры используют URL с протоколом blob:. Если ваше приложение применяет политику безопасности содержимого (Content Security Policy), убедитесь, что директива img-src включает blob:.
Полноценные объекты загрузки работают благодаря JavaScript-синтезаторам. При отправке обратно на сервер или преобразовании через JSON.stringify() они автоматически преобразуются в своё исходное wire-значение.
Загрузка файлов без использования поля выбора файлов⚓︎
Современные интерфейсы позволяют загружать файлы не только через поле выбора файлов: пользователи вставляют скриншоты в поле сообщения, перетаскивают файлы на страницу или нажимают кнопку «Прикрепить», которая открывает системный диалог выбора файлов.
Livewire поддерживает все эти сценарии с помощью одного действия — $upload. Используйте его в обработчиках любых событий, чаще всего с wire:paste, wire:drop и wire:click:
<textarea wire:paste="$upload('photos')"></textarea>
<div wire:drop.file="$upload('photos')">Перетащите файлы сюда</div>
<button type="button" wire:click="$upload('photos')">Прикрепить файлы</button>
$upload работает по одному простому правилу: сначала используются явно переданные файлы, затем файлы из события, а если их нет — запрашиваются у пользователя.
- Если событие, вызвавшее действие, содержит файлы — например, при вставке скриншотов или перетаскивании файлов, — именно эти файлы будут загружены в свойство.
- Если событие может содержать файлы, но не содержит их — например, при вставке обычного текста, — ничего не произойдёт, и браузер продолжит выполнять своё стандартное поведение.
- Если событие не может содержать файлы — например, при клике, — откроется диалог выбора файлов, после чего будут загружены выбранные пользователем файлы.
Поскольку $upload самостоятельно выполняет необходимую фильтрацию, обработчики остаются обычными обработчиками событий, которые можно использовать как угодно.
Модификатор .file у wire:drop в примере выше — это фильтр на уровне обработчика, аналогичный wire:keydown.enter. Он дополнительно ограничивает зону перетаскивания только теми случаями, когда перетаскиваются файлы, поэтому при перетаскивании выделенного текста она вообще не будет реагировать. Подробнее об этом см. в документации wire:drop.
.file работает с любыми обработчиками событий, которые могут содержать файлы. Например, wire:paste.file="handlePastedFiles" сработает только для вставки, если буфер обмена содержит файлы. Это удобно для пользовательских обработчиков, которым иначе пришлось бы самостоятельно выполнять такую проверку.
Загрузки, начатые таким способом, являются обычными загрузками Livewire: они проходят через тот же механизм временных загрузок, что и поля выбора файлов с wire:model, учитывают ваши правила валидации и гидратируются в полноценные объекты загрузки, обеспечивая мгновенные предпросмотры и отображение прогресса.
Ниже приведён полный пример поля ввода сообщений в стиле чата, которое поддерживает прикрепление файлов всеми тремя способами:
<?php // resources/views/components/⚡message-box.blade.php
use Livewire\Attributes\Validate;
use Livewire\WithFileUploads;
use Livewire\Component;
new class extends Component {
use WithFileUploads;
public $message = '';
#[Validate(['attachments.*' => 'file|max:10240'])]
public $attachments = [];
public function send()
{
// ...
}
};
<div class="relative" wire:drop.file.window="$upload('attachments')">
{ Полноэкранный оверлей, отображаемый, пока файлы перетаскиваются над страницей... }
<div class="hidden in-data-dragging:grid fixed inset-0 place-items-center bg-black/50 text-white">
Перетащите файлы, чтобы прикрепить их
</div>
<form wire:submit="send">
{ Ожидающие и уже загруженные вложения, представленные полноценными объектами загрузки... }
<template wire:for="file in attachments" wire:for:key="file.name">
<div>
<img wire:show="file.isPreviewable" wire:bind:src="file.previewUrl">
<span wire:text="file.name"></span>
<progress wire:show="file.isUploading" wire:bind:value="file.progress" max="100"></progress>
<button type="button" wire:click="file.remove()">×</button>
</div>
</template>
<textarea wire:model="message" wire:paste="$upload('attachments')"></textarea>
<button type="button" wire:click="$upload('attachments', { accept: 'image/*,.pdf' })">
Добавить фотографии и файлы
</button>
<button type="submit" wire:bind:disabled="attachments.some(file => file.isUploading)">
Отправить
</button>
</form>
</div>
Стоит обратить внимание на несколько моментов:
wire:pasteразмещается на элементах — события вставки всплывают, поэтому, если повесить его на<form>, оно будет обрабатывать вставку для всех полей внутри формы.- Модификатор
.fileуwire:dropограничивает зону перетаскивания только случаями, когда перетаскиваются файлы, — при перетаскивании выделенного текста по странице оверлей не будет мигать. .windowпринимает файлы, перетаскиваемые в любую область страницы, а атрибутdata-dragging, который он добавляет, используется для отображения оверлея с помощью обычного CSS — без какого-либо JavaScript.- Кнопка «Добавить фотографии и файлы» является настоящим элементом
<button>, поэтому поддержка клавиатуры и программ чтения с экрана работает автоматически — никаких скрытых<input type="file">не требуется.
Параметры⚓︎
$upload принимает объект параметров в качестве последнего аргумента:
<button type="button" wire:click="$upload('attachments', { accept: 'image/*', multiple: true })">
Добавить фотографии
</button>
| Параметр | Описание |
|---|---|
accept |
Фильтрует принимаемые файлы так же, как атрибут accept у обычного поля выбора файлов — принимает MIME-типы (с поддержкой подстановочных символов) или расширения, перечисленные через запятую. Применяется как к файлам, выбранным через диалог, так и к файлам, вставленным или перетащенным |
multiple |
Разрешать ли выбор нескольких файлов. По умолчанию — true, если свойство в данный момент содержит массив, иначе false |
append |
Определяет, следует ли при множественной загрузке добавлять файлы к уже существующим в свойстве (true, значение по умолчанию) или заменять их |
Клиентская фильтрация — это удобство, а не защита
Как и атрибут accept у обычного поля выбора файлов, фильтрация в $upload лишь улучшает пользовательский опыт для добросовестных пользователей. Всегда проверяйте файлы с помощью валидации на стороне сервера.
Явная передача файлов и событий⚓︎
$upload также принимает файлы — или событие, из которого их можно извлечь, — в качестве второго аргумента, если вы настраиваете всё вручную:
Тестирование загрузки файлов⚓︎
Вы можете использовать стандартные вспомогательные методы 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
{
public function test_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');
}
}
Ниже приведён пример компонента upload-photo, который требуется для того, чтобы предыдущий тест прошёл успешно:
<?php
use Livewire\WithFileUploads;
use Livewire\Component;
new class extends Component {
use WithFileUploads;
public $photo;
public function upload($name)
{
$this->photo->storeAs('/', $name, disk: 'avatars');
}
// ...
};
Подробную информацию о тестировании загрузки файлов смотрите в документации Laravel по тестированию загрузки файлов.
Использование S3⚓︎
Всё в этом разделе в равной степени относится как к Amazon S3, так и к S3-совместимым сервисам, таким как Cloudflare R2 и DigitalOcean Spaces.
Перед настройкой важно понимать, что каждая загрузка файла через Livewire проходит через два этапа:
- Временное хранение — в момент выбора файла пользователем Livewire загружает его во временную директорию (
livewire-tmp/), чтобы файл можно было проверить и показать предпросмотр. Эта часть находится под управлением Livewire. - Постоянное хранение — ничего не сохраняется окончательно, пока ваш код не вызовет
->store(). Куда именно этот вызов поместит файл, полностью зависит от вас.
Эти два этапа настраиваются независимо друг от друга, и «использование S3» может означать одно или оба из следующих вариантов:
- Если вы просто хотите, чтобы загруженные файлы оказались в S3, вам нужны только шаги 1 и 2.
- Если вы также хотите, чтобы сами загрузки обходили ваш сервер и сразу отправлялись в ваш bucket, переходите к шагу 3.
Шаг 1: Настройте диск S3⚓︎
Laravel поставляется с диском s3 в config/filesystems.php, который уже настроен через переменные окружения, поэтому обычно вам не требуется изменять сам конфигурационный файл.
Сначала установите адаптер S3 для Flysystem — по умолчанию он не включён в Laravel:
Затем укажите учётные данные в вашем файле .env.
Для Amazon S3:
AWS_ACCESS_KEY_ID=your-key-id
AWS_SECRET_ACCESS_KEY=your-secret-key
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=your-bucket-name
Для S3-совместимого сервиса также укажите endpoint этого сервиса. Например, для Cloudflare R2:
AWS_ACCESS_KEY_ID=your-r2-access-key-id
AWS_SECRET_ACCESS_KEY=your-r2-secret-key
AWS_DEFAULT_REGION=auto
AWS_BUCKET=your-bucket-name
AWS_ENDPOINT=https://<account-id>.r2.cloudflarestorage.com
Или для DigitalOcean Spaces:
AWS_ACCESS_KEY_ID=your-spaces-key
AWS_SECRET_ACCESS_KEY=your-spaces-secret
AWS_DEFAULT_REGION=nyc3
AWS_BUCKET=your-space-name
AWS_ENDPOINT=https://nyc3.digitaloceanspaces.com
Проверьте диск перед дальнейшей настройкой
Запустите php artisan tinker и попробуйте записать файл:
Если результатом будет true, значит ваши учётные данные работают, и всё, что описано ниже, также будет работать. Исправить опечатку в секрете на этом этапе можно за несколько секунд — искать причину через неудачную загрузку файла гораздо дольше.
Шаг 2: Сохраняйте загруженные файлы в S3⚓︎
Куда файл попадёт для постоянного хранения, определяется вызовом ->store(), а не какой-либо настройкой Livewire. Передайте имя диска, чтобы сохранить файл в S3:
<?php
public function save()
{
$this->validate();
$this->photo->store(path: 'photos', options: 's3');
}
Теперь загруженные файлы будут попадать в директорию photos/ вашего bucket. Если это всё, что вам требовалось, настройка завершена.
На этом этапе временные загрузки — промежуточный этап между выбором файла пользователем и выполнением вашего метода save() — всё ещё хранятся на сервере приложения. Для большинства приложений это вполне нормально. Если вы хотите полностью исключить свой сервер из процесса загрузки, переходите к шагу 3.
Шаг 3 (необязательно): Отправляйте временные загрузки напрямую в S3⚓︎
По умолчанию Livewire хранит временные загрузки на локальном диске, а это означает, что каждая загрузка проходит через сервер приложения — даже если впоследствии файл будет окончательно сохранён в S3.
Чтобы обойти ваш сервер, укажите S3-диск для временных загрузок Livewire в вашем файле .env:
Теперь, когда пользователь выбирает файл, браузер загружает его напрямую в директорию livewire-tmp/ вашего bucket с использованием предварительно подписанного URL — файл вообще не проходит через ваш сервер. Предпросмотры изображений через ->temporaryUrl() также предоставляются напрямую из S3, а большие файлы автоматически используют нативные multipart-загрузки S3 (см. чанковые и возобновляемые загрузки).
Поскольку теперь браузер напрямую взаимодействует с вашим bucket, в нём необходимо разрешить кросс-доменные PUT-запросы с домена вашего приложения. Добавьте CORS-политику для bucket:
[
{
"AllowedOrigins": ["https://your-app.com"],
"AllowedMethods": ["PUT", "GET"],
"AllowedHeaders": ["*"],
"MaxAgeSeconds": 3000
}
]
В Amazon S3 это находится в настройках bucket: Permissions → Cross-origin resource sharing (CORS); в Cloudflare R2 — в разделе Settings → CORS policy. Без этого загрузки будут завершаться ошибками CORS в браузере, даже если вся серверная часть настроена правильно.
Поддерживаются как однофайловые (public $photo), так и многофайловые (public $photos = []) загрузки — каждый файл загружается напрямую в S3 через свой собственный URL с предварительной подписью.
Совет
Для полного контроля над поведением временных загрузок — диском, директорией, правилами валидации и прочим — опубликуйте конфигурационный файл Livewire с помощью php artisan livewire:config и отредактируйте раздел temporary_file_upload.
Настройка автоматической очистки файлов⚓︎
Когда временные загрузки хранятся в S3, Livewire не может очищать их так же, как это происходит при локальном хранении, поэтому директория livewire-tmp/ быстро заполнится файлами. Вместо этого следует настроить сам S3 на удаление файлов старше 24 часов.
Чтобы настроить это, выполните следующую Artisan-команду из окружения, которое использует S3 bucket для временных загрузок:
Теперь любые временные файлы старше 24 часов будут автоматически удаляться из S3.
Информация
Если вы не используете S3 для временных загрузок, Livewire автоматически управляет очисткой файлов, и выполнять команду выше не требуется.
Чанковые и возобновляемые загрузки⚓︎
Большие файлы автоматически загружаются частями — никаких изменений конфигурации или разметки не требуется.
Когда выбранный файл больше настроенного порога чанкинга, Livewire разбивает его в браузере на части и загружает их по одной, затем собирает файл обратно и выполняет его проверку на сервере. На S3-дисках Livewire вместо этого использует нативные multipart-загрузки S3, поэтому большие файлы по-прежнему полностью обходят сервер приложения.
Чанковые загрузки решают две давние проблемы загрузки файлов:
- Ограничения PHP на загрузку больше не применяются. Поскольку размер каждого чанка меньше значения
upload_max_filesizeв стандартномphp.ini, пользователи могут загружать файлы, значительно превышающие ограничения вашей PHP-конфигурации. При этом именно ваши правила валидации Livewire (например,max:) остаются источником истины для определения допустимого размера файла. - Прерванные загрузки можно возобновить. Если загрузка была отменена, прервана или страница была перезагружена во время процесса, повторный выбор того же файла продолжит загрузку с того места, где она остановилась — Livewire вычисляет отпечаток файла и загружает только те чанки, которых ещё нет на сервере.
Чанковые загрузки защищены так же, как и обычные временные загрузки: каждый запрос чанка содержит криптографически подписанную ссылку, в которой зашифрованы идентификатор загрузки, количество чанков и размер чанка — клиент не может изменить эти данные. Кроме того, отпечатки ограничены областью сессии пользователя, поэтому один пользователь никогда не сможет получить доступ к незавершённой загрузке другого пользователя.
Вы можете настроить это поведение в разделе temporary_file_upload конфигурационного файла Livewire:
<?php
'temporary_file_upload' => [
// ...
'chunking' => true, // Установите false, чтобы всегда загружать файлы целиком...
'chunk_size' => null, // Количество байт в одном чанке | По умолчанию: 1 МБ (5 МБ для S3 — минимальный размер multipart-части)
'chunk_threshold' => null, // Файлы больше этого значения разбиваются на чанки | По умолчанию: chunk_size
],
Пользовательский мидлвар загрузки и ограничение частоты запросов
Один файл при чанковой загрузке отправляется множеством небольших запросов, поэтому endpoint для чанков использует более высокий лимит по умолчанию (throttle:600,1). Если вы задаёте собственный middleware для temporary_file_upload, он также применяется к endpoint чанков — строгое ограничение вроде throttle:60,1 приведёт к сбою больших загрузок. Устанавливайте более щедрый лимит или отключите chunking, если эта функция вам не нужна.
Брошенные multipart-загрузки S3
Незавершённые multipart-загрузки в S3 занимают скрытое место в хранилище, пока не будут отменены. Добавьте правило жизненного цикла AbortIncompleteMultipartUpload для вашего bucket (один день — хорошее значение по умолчанию), чтобы они автоматически удалялись. На дисках, отличных от S3, Livewire очищает устаревшие чанки вместе с другими временными загрузками.
Индикаторы загрузки⚓︎
Хотя wire:model для загрузки файлов работает под капотом иначе, чем для остальных типов полей ввода с wire:model, интерфейс отображения индикаторов загрузки остаётся тем же самым.
Вы можете показать индикатор загрузки, привязанный именно к загрузке файла, с помощью директивы wire:loading:
Или ещё проще — используя автоматический атрибут data-loading, который добавляет Livewire:
<div>
<input type="file" wire:model="photo">
<div class="not-data-loading:hidden">Загрузка...</div>
</div>
Теперь, пока идёт загрузка файла, будет отображаться сообщение «Загрузка...», а после завершения загрузки оно автоматически скроется.
Подробнее о состояниях загрузки →
Индикаторы прогресса⚓︎
Каждая операция загрузки файла в Livewire отправляет специальные JavaScript-события на соответствующий элемент <input>, что позволяет вашему собственному JavaScript-коду перехватывать эти события:
| Событие | Описание |
|---|---|
livewire-upload-start |
Отправляется в момент начала загрузки |
livewire-upload-finish |
Отправляется при успешном завершении загрузки |
livewire-upload-cancel |
Отправляется, если загрузка была прервана досрочно |
livewire-upload-error |
Отправляется при ошибке загрузки |
livewire-upload-progress |
Отправляется периодически во время загрузки, содержит процент прогресса |
Ниже приведён пример обёртывания поля загрузки файла Livewire в компонент Alpine.js для отображения полосы прогресса загрузки:
<form wire:submit="save">
<div
x-data="{ uploading: false, progress: 0 }"
x-on:livewire-upload-start="uploading = true"
x-on:livewire-upload-finish="uploading = false"
x-on:livewire-upload-cancel="uploading = false"
x-on:livewire-upload-error="uploading = false"
x-on:livewire-upload-progress="progress = $event.detail.progress"
>
<!-- Поле ввода файла -->
<input type="file" wire:model="photo">
<!-- Полоса прогресса -->
<div x-show="uploading">
<progress max="100" x-bind:value="progress"></progress>
</div>
</div>
<!-- ... -->
</form>
Отмена загрузки⚓︎
Если загрузка занимает слишком много времени, пользователь может захотеть её отменить. Эту функциональность можно реализовать с помощью функции $cancelUpload() в JavaScript, предоставляемой Livewire.
Вот пример создания кнопки «Отменить загрузку» в компоненте Livewire с использованием wire:click для обработки клика:
<form wire:submit="save">
<!-- Поле ввода файла -->
<input type="file" wire:model="photo">
<!-- Кнопка отмены загрузки -->
<button type="button" wire:click="$cancelUpload('photo')">Отменить загрузку</button>
<!-- ... -->
</form>
Когда нажата кнопка «Отменить загрузку», запрос на загрузку файла будет прерван, а поле ввода файла — очищено. Пользователь теперь может попытаться загрузить другой файл.
Альтернативно, вы можете вызвать метод cancelUpload(...) из Alpine.js следующим образом:
JavaScript API загрузки⚓︎
Интеграция со сторонними библиотеками загрузки файлов часто требует большего контроля, чем простое использование элемента <input type="file" wire:model="...">.
Для таких случаев Livewire предоставляет специальные JavaScript-функции.
Эти функции доступны на объекте JavaScript-компонента, который можно удобно получить через объект $wire внутри шаблона вашего Livewire-компонента:
<script>
// Открыть диалог выбора файла и загрузить выбранный пользователем файл...
await $wire.$upload('photos')
// Загрузить конкретные объекты File (или FileList, или массив объектов File)...
await $wire.$upload('photos', files)
// Получить файлы из события вставки, перетаскивания или изменения...
await $wire.$upload('photos', event)
// Передать параметры выбора файлов и фильтрации...
await $wire.$upload('photos', { accept: 'image/*', multiple: true })
// Удалить один файл из списка загруженных файлов...
$wire.$removeUpload('photos', uploadedFilename)
// Отменить загрузку...
$wire.$cancelUpload('photos')
</script>
$wire.$upload() возвращает промис, который разрешается в полноценный объект загрузки, теперь находящийся в свойстве, — либо в массив таких объектов при загрузке нескольких файлов (только файлов из текущей загрузки, а не ранее загруженных файлов, уже находящихся в свойстве). Если пользователь закроет диалог выбора файла или отменит загрузку, промис разрешится значением null. Если загрузка завершится ошибкой, промис будет отклонён.
<script>
let photo = await $wire.$upload('photo')
photo.previewUrl // Мгновенный локальный предпросмотр...
</script>
Устаревшая сигнатура с колбэками из предыдущих версий Livewire по-прежнему поддерживается:
<script>
$wire.$upload('photo', file, (uploadedFilename) => {
// Коллбэк успешной загрузки...
}, () => {
// Коллбэк ошибки...
}, (event) => {
// Коллбэк прогресса...
// event.detail.progress содержит число от 1 до 100 по мере загрузки
}, () => {
// Коллбэк отмены...
})
// Загрузка нескольких файлов...
$wire.$uploadMultiple('photos', [file], successCallback, errorCallback, progressCallback, cancelledCallback)
</script>
Конфигурация⚓︎
Поскольку Livewire сохраняет все загруженные файлы временно до того, как разработчик их проверит или сохранит, он предполагает некоторое поведение обработки по умолчанию для всех загрузок файлов.
Глобальная валидация⚓︎
По умолчанию Livewire валидирует все временные загруженные файлы с помощью следующих правил: file|max:12288 (должен быть файл размером менее 12 МБ).
Если вы хотите изменить эти правила, сделать это можно в файле конфигурации вашего приложения config/livewire.php:
<?php
'temporary_file_upload' => [
// ...
'rules' => 'file|mimes:png,jpg,pdf|max:102400', // (максимум 100 МБ, принимаются только PNG, JPEG и PDF)
],
Глобальный мидлвар⚓︎
Конечная точка временной загрузки файлов по умолчанию оснащена мидлваром для ограничения частоты запросов. Вы можете настроить, какой именно мидлвар будет применяться к этой конечной точке, через следующую опцию конфигурации:
<?php
'temporary_file_upload' => [
// ...
'middleware' => 'throttle:5,1', // Разрешить только 5 загрузок на пользователя в минуту
],
Директория временных загрузок⚓︎
Временные файлы загружаются в директорию livewire-tmp/ указанного диска. Вы можете изменить эту директорию с помощью следующей опции конфигурации:
Смотрите также⚓︎
- Формы — Обработка загрузки файлов в формах
- Валидация — Проверка загруженных файлов
- Состояния загрузки — Отображение индикаторов прогресса загрузки
- wire:model — Привязка полей ввода файлов к свойствам