文件上传
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>
请注意,上述示例使用“保存”方法而不是“上传”方法。这是一个常见的“陷阱”。“上传”一词是 Livewire 保留的。您不能在组件中将其用作方法或属性名称。
从开发人员的角度来看,处理文件输入与处理任何其他输入类型没有什么不同:将 wire:model
添加到 <input>
标记,其他所有内容都会为您处理。
但是,在 Livewire 中使文件上传起作用,幕后还有更多的事情在发生。以下是用户选择要上传的文件时发生的情况:
- 当选择新文件时,Livewire 的 JavaScript 会向服务器上的组件发出初始请求以获取临时的“签名”上传 URL。
- 收到 URL 后,JavaScript 会将实际“上传”操作执行到签名 URL,将上传内容存储在 Livewire 指定的临时目录中,并返回新临时文件的唯一哈希 ID。
- 文件上传并生成唯一哈希 ID 后,Livewire 的 JavaScript 会向服务器上的组件发出最终请求,告诉它将所需的公共属性“设置”为新的临时文件。
- 现在,公共属性(在本例中为
$photo
)已设置为临时文件上传,并随时可以存储或验证。
存储上传的文件
上一个示例演示了最基本的存储场景:将临时上传的文件移动到应用程序默认文件系统磁盘上的“照片”目录。
但是,您可能希望自定义存储文件的的文件名,甚至指定一个特定的存储“磁盘”来保存文件(例如 S3)。
Livewire 遵循 Laravel 用于存储上传文件的相同 API,因此请随时查阅 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');}
处理多个文件
Livewire 通过检测 <input>
标记上的 multiple
属性自动处理多个文件上传。
例如,下面是一个具有名为 $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>
文件验证
正如我们所讨论的,使用 Livewire 验证文件上传与从标准 Laravel 控制器处理文件上传相同。
许多与文件相关的验证规则需要访问文件。当 直接上传到 S3 时,如果 S3 文件对象不可公开访问,这些验证规则将失败。
有关文件验证的更多信息,请查阅 Laravel 的文件验证文档。
临时预览 URL
在用户选择文件后,你通常应在他们提交表单并存储文件之前向他们显示该文件的预览。
Livewire 通过对上传的文件使用 ->temporaryUrl()
方法使此操作变得非常简单。
出于安全原因,仅支持具有图像 MIME 类型的文件使用临时上传 URL。
我们来探索一个带有图像预览的文件上传示例
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 来预览系统上的其他文件。
如果你已将 Livewire 配置为使用 S3 进行临时文件存储,则调用 ->temporaryUrl()
将直接生成一个临时签名 URL 到 S3,以便图像预览不会从你的 Laravel 应用程序服务器加载。
测试文件上传
你可以使用 Laravel 现有的文件上传测试助手来测试文件上传。
下面是使用 Livewire 测试 UploadPhoto
组件的完整示例
<?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 的文件上传测试文档。
直接上传到 Amazon S3
如前所述,Livewire 将所有文件上传存储在临时目录中,直到开发人员永久存储该文件。
默认情况下,Livewire 使用默认文件系统磁盘配置(通常为 local
),并将文件存储在 livewire-tmp/
目录中。
因此,即使你选择稍后将上传的文件存储在 S3 存储桶中,文件上传也始终会使用你的应用程序服务器。
如果你希望绕过应用程序服务器,而将 Livewire 的临时上传存储在 S3 存储桶中,则可以在应用程序的 config/livewire.php
配置文件中配置该行为。首先,将 livewire.temporary_file_upload.disk
设置为 s3
(或使用 s3
驱动程序的另一个自定义磁盘)
return [ // ... 'temporary_file_upload' => [ 'disk' => 's3', // ... ],];
现在,当用户上传文件时,该文件实际上永远不会存储在你的服务器上。相反,它将直接上传到 livewire-tmp/
子目录中的 S3 存储桶中。
在自定义文件上传磁盘之前,你必须首先通过运行以下命令将 Livewire 的配置文件发布到应用程序的 /config
目录
php artisan livewire:publish --config
配置自动文件清理
Livewire 的临时上传目录会很快填满文件;因此,配置 S3 以清理超过 24 小时的文件非常重要。
要配置此行为,请从使用 S3 存储桶进行文件上传的环境中运行以下 Artisan 命令
php artisan livewire:configure-s3-upload-cleanup
现在,任何超过 24 小时的临时文件都将由 S3 自动清理。
如果您没有使用 S3 进行文件存储,Livewire 将自动处理文件清理,无需运行上述命令。
加载指示器
虽然文件上传的 wire:model
在底层与其他 wire:model
输入类型的工作方式不同,但显示加载指示器的界面保持不变。
您可以像这样显示针对文件上传的加载指示器
<input type="file" wire:model="photo"> <div wire:loading wire:target="photo">Uploading...</div>
现在,在文件上传期间,将显示“正在上传...”消息,然后在上传完成后将其隐藏。
有关加载状态的更多信息,请查看我们的综合 加载状态文档。
进度指示器
每次 Livewire 文件上传操作都会在相应的 <input>
元素上分派 JavaScript 事件,允许自定义 JavaScript 拦截这些事件
事件 | 说明 |
---|---|
livewire-upload-start |
上传开始时分派 |
livewire-upload-finish |
上传成功完成后分派 |
livewire-upload-cancel |
上传过早取消时分派 |
livewire-upload-error |
上传失败时分派 |
livewire-upload-progress |
包含上传进度百分比的事件,随着上传的进行而进行 |
下面是一个将 Livewire 文件上传包装在 Alpine 组件中以显示上传进度条的示例
<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" > <!-- File Input --> <input type="file" wire:model="photo"> <!-- Progress Bar --> <div x-show="uploading"> <progress max="100" x-bind:value="progress"></progress> </div> </div> <!-- ... --></form>
取消上传
如果上传需要很长时间,用户可能希望取消它。您可以使用 Livewire 在 JavaScript 中的 $cancelUpload()
函数提供此功能。
下面是一个在 Livewire 组件中创建“取消上传”按钮的示例,使用 wire:click
来处理点击事件
<form wire:submit="save"> <!-- File Input --> <input type="file" wire:model="photo"> <!-- Cancel upload button --> <button type="button" wire:click="$cancelUpload('photo')">Cancel Upload</button> <!-- ... --></form>
按下“取消上传”时,文件上传请求将被中止,文件输入将被清除。用户现在可以使用不同的文件尝试再次上传。
或者,你可以像这样从 Alpine 调用 cancelUpload(...)
<button type="button" x-on:click="$wire.cancelUpload('photo')">Cancel Upload</button>
JavaScript 上传 API
与第三方文件上传库集成通常需要比简单的 <input type="file" wire:model="...">
元素更多的控制。
对于这些场景,Livewire 公开了专门的 JavaScript 函数。
这些函数存在于 JavaScript 组件对象中,可以使用 Livewire 方便的 $wire
对象从 Livewire 组件的模板中访问该对象
@script<script> let file = $wire.el.querySelector('input[type="file"]').files[0] // Upload a file... $wire.upload('photo', file, (uploadedFilename) => { // Success callback... }, () => { // Error callback... }, (event) => { // Progress callback... // event.detail.progress contains a number between 1 and 100 as the upload progresses }, () => { // Cancelled callback... }) // Upload multiple files... $wire.uploadMultiple('photos', [file], successCallback, errorCallback, progressCallback, cancelledCallback) // Remove single file from multiple uploaded files... $wire.removeUpload('photos', uploadedFilename, successCallback) // Cancel an upload... $wire.cancelUpload('photos')</script>@endscript
配置
由于 Livewire 在开发人员验证或存储所有文件上传之前会暂时存储它们,因此它假定所有文件上传都有一些默认处理行为。
全局验证
默认情况下,Livewire 将使用以下规则验证所有临时文件上传:file|max:12288
(必须小于 12MB 的文件)。
如果你希望自定义这些规则,可以在应用程序的 config/livewire.php
文件中进行自定义
'temporary_file_upload' => [ // ... 'rules' => 'file|mimes:png,jpg,pdf|max:102400', // (100MB max, and only accept PNGs, JPEGs, and PDFs)],
全局中间件
默认情况下,临时文件上传端点被分配了一个节流中间件。你可以通过以下配置选项自定义此端点使用的中间件
'temporary_file_upload' => [ // ... 'middleware' => 'throttle:5,1', // Only allow 5 uploads per user per minute],
临时上传目录
临时文件被上传到指定磁盘的 livewire-tmp/
目录。你可以通过以下配置选项自定义此目录
'temporary_file_upload' => [ // ... 'directory' => 'tmp',],