Livewire 2.x ファイルアップロード

{video} 細かに説明したスクリーンキャストを用意している、Livewireに驚いてください

基本的なファイルアップロード

{note} この機能を使用するにはLivewireのバージョン1.2.0以上が必要です。

Livewireを使用すると、ファイルのアップロードと保存が非常に簡単になります。

まずWithFileUploadsトレイトをコンポーネントに追加します。これにより、ファイル入力で他の入力タイプと同じように wire:modelを使用でき、残りはLivewireが処理します。

写真をアップロード処理する、シンプルなコンポーネント例をご覧ください。

use Livewire\WithFileUploads;

class UploadPhoto extends Component
{
    use WithFileUploads;

    public $photo;

    public function save()
    {
        $this->validate([
            'photo' => 'image|max:1024', // 最大1MB
        ]);

        $this->photo->store('photos');
    }
}
<form wire:submit.prevent="save">
    <input type="file" wire:model="photo">

    @error('photo') <span class="error">{{ $message }}</span> @enderror

    <button type="submit">Save Photo</button>
</form>

開発者の視点では、ファイル入力の処理は他の入力タイプの処理と同じです。wire:model<input>タグに追加すれば他のすべてが自動的に処理されます。

ただし、Livewireでファイルのアップロードを機能させるために、内部ではとても多くのことが行われています。ユーザーがアップロードするファイルを選択したときに何が起こるかをざっと見てみましょう。

  1. 新しいファイルが選択されると、LivewireのJavaScriptはサーバー上のコンポーネントに最初の要求を行い、一時的な「署名済み」アップロードURLを取得します。
  2. URLを受信すると、JavaScriptは署名されたURLへの実際の「アップロード」を実行します。Livewireが用意した一時ディレクトリにアップロードを保存し、新しい一時ファイルの一意のハッシュIDを返します。
  3. ファイルがアップロードされ、一意のハッシュIDが生成されると、LivewireのJavaScriptはサーバー上のコンポーネントに最終的な要求を行い、希望するパブリックプロパティを新しい一時ファイルに「設定」するように指示します。
  4. これで、パブリックプロパティ(この場合は$photo)が一時ファイルのアップロードに設定され、いつでも保存または検証できるようになりました。

アップロードファイルの保存

前例は、最も基本的な保存シナリオを示しています。一時的にアップロードされたファイルをアプリケーションのデフォルトファイルシステムディスクの"photos"ディレクトリに移動します。

しかし、保存するファイルのファイル名をカスタマイズしたり、ファイルを保存するストレージ「ディスク」を指定したりすることもできます。(たとえば、S3バケット)

LivewireはLaravelが使用しているAPIを尊重するため、アップロードしたファイルを保存する同じAPIを使っています。Laravelのドキュメントを参照してください。ただし、一般的な保存シナリオは次のとおりです。

// アップロードしたファイルをデフォルトのファイルシステムディスクの"photos"ディレクトリに保存します
$this->photo->store('photos');

// 設定済みの"s3"バケットの"photos"ディレクトリに保存します。
$this->photo->store('photos', 's3');

// ファイル名"avatar.png"で"photos"ディレクトリに保存します。
$this->photo->storeAs('photos', 'avatar');

// 設定済みのs3のバケットの"photos"ディレクトリに"avatar.png"というファイル名で保存します。
$this->photo->storeAs('photos', 'avatar', 's3');

// 設定済みの"s3"バケットに"public"の可視性を持たせ、"photos"ディレクトリに保存します。
$this->photo->storePublicly('photos', 's3');

// 設定済みの"s3"バケットに"public"の可視性を持たせて、"avatar.png"という名前で"photos"ディレクトリに保存します。
$this->photo->storePubliclyAs('photos', 'avatar', 's3');

上記メソッドは、アップロードされたファイルを望みどおりに保存するための柔軟性を十分に提供しているでしょう。

複数ファイルの処理

Livewireは、<input>タグの multiple属性を検出し、複数ファイルのアップロードを自動的に処理します。

複数アップロードを処理するファイルアップロードの例を以下に示します。

use Livewire\WithFileUploads;

class UploadPhotos extends Component
{
    use WithFileUploads;

    public $photos = [];

    public function save()
    {
        $this->validate([
            'photos.*' => 'image|max:1024', // 最大1MB
        ]);

        foreach ($this->photos as $photo) {
            $photo->store('photos');
        }
    }
}
<form wire:submit.prevent="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コントローラーからのファイルのアップロード処理とまったく同じです。

Laravelのファイルバリデーション機能の詳細は、ドキュメントをご覧ください。

リアルタイムバリデーション

ユーザーが「送信」を押す前に、ユーザーのアップロードをリアルタイムでバリデートできます。

繰り返しますが、Livewireの他の入力タイプと同様に実現できます。

use Livewire\WithFileUploads;

class UploadPhoto extends Component
{
    use WithFileUploads;

    public $photo;

    public function updatedPhoto()
    {
        $this->validate([
            'photo' => 'image|max:1024', // 最大1MB
        ]);
    }

    public function save()
    {
        // ...
    }
}
<form wire:submit.prevent="save">
    <input type="file" wire:model="photo">

    @error('photo') <span class="error">{{ $message }}</span> @enderror

    <button type="submit">Save Photo</button>
</form>

これで、ユーザーがファイルを選択すると(Livewireがファイルを一時ディレクトリにアップロードした後)、そのファイルが検証され、ユーザーはフォーム送信の前にエラーを受け取ります。

一時的なプレビューのURL

ユーザーがファイルを選択した後、フォームを送信して実際にファイルを保存する前に、そのファイルのプレビュー表示をおすすめします。

Livewireでは、アップロード済みファイルの->temporaryUrl()メソッドで簡単にできます。

{note} セキュリティ上の理由から、一時URLは画像のアップロードでのみサポートされています。

画像プレビュー付きファイルアップロードの1例をご覧ください。

use Livewire\WithFileUploads;

class UploadPhotoWithPreview extends Component
{
    use WithFileUploads;

    public $photo;

    public function updatedPhoto()
    {
        $this->validate([
            'photo' => 'image|max:1024',
        ]);
    }

    public function save()
    {
        // ...
    }
}
<form wire:submit.prevent="save">
    @if ($photo)
        Photo Preview:
        <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を悪用してシステム上の他のファイルをプレビューすることはできません。

{tip} 一時ファイルストレージにS3を使用するようにLivewireを設定した場合、->temporaryUrl()を呼び出すとS3からの一時的な署名付きURLが直接生成されるため、このプレビューによりLaravelアプリサーバーに対するアクセスは発生しません。

ファイルアップロードのテスト

Livewireでのファイルアップロードのテストは、Laravelのファイルアップロードテストヘルパーを使用すると簡単です。

これは、Livewireを使用して"UploadPhoto"コンポーネントをテストする完全な例です。

/** @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.php

これは、前記のテストにパスするために必要な"UploadPhoto"コンポーネントのスニペットです。

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.disks3(もしくはs3ドライバーを使用する別のカスタムディスク)に設定します。

return [
    ...
    'temporary_file_upload' => [
        'disk' => 's3',
        ...
    ],
];

これで、ユーザーがファイルをアップロードしたとき、実際にファイルがサーバーにアクセスすることはありません。サブディレクトリlivewire-tmp/の下のS3バケットに直接アップロードされます。

自動ファイルクリーンアップの設定

この一時ディレクトリはファイルですぐに一杯になるでしょう。24時間以上経過したファイルをクリーンアップするようにS3を設定することが重要です。

この動作を設定するには、S3バケットが設定されている環境から次のArtisanコマンドを実行するだけです。

php artisan livewire:configure-s3-upload-cleanup

これで、24時間より古い一時ファイルは、S3によって自動的にクリーンアップされます。

{tip} S3を使用しいない場合、Livewireはファイルのクリーンアップを自動的に処理します。このコマンドを実行する必要はありません。

読み込みインジケーター

ファイルアップロードのwire:modelは、他のwire:model入力タイプとは内部動作は異なりますが、読み込みインジケーターを表示するためのインターフェイスは同じです。

以下のように、ファイルのアップロードを対象とする、読み込みインジケータを表示できます。

<input type="file" wire:model="photo">

<div wire:loading wire:target="photo">Uploading...</div>

これで、ファイルのアップロード中に"Uploading..."というメッセージが表示され、アップロードが完了すると表示が消えます。

これは、Livewire ロード状況API全体にで実現しています。

進行状況インジケーター(およびすべてのJavaScriptイベント)

カスタムJavaScriptがリッスンできるように、Livewireでファイルをアップロードするたびに、<input>要素へJavaScriptイベントがディスパッチされます。

ディスパッチされるイベントは以下のとおりです。

イベント 説明
livewire-upload-start アップロード開始時に発行します
livewire-upload-finish アップロードが正常に終了した場合に発行します
livewire-upload-error アップロードが何らかの方法で失敗した場合に発行します
livewire-upload-progress アップロードの進行に応じて、アップロードの進行状況のパーセンテージを含むイベントを発行します

これは、LivewireファイルのアップロードをAlpineJSコンポーネントでラップして、プログレスバーを表示する例です。

<div
    x-data="{ isUploading: false, progress: 0 }"
    x-on:livewire-upload-start="isUploading = true"
    x-on:livewire-upload-finish="isUploading = false"
    x-on:livewire-upload-error="isUploading = false"
    x-on:livewire-upload-progress="progress = $event.detail.progress"
>
    <!-- File Input -->
    <input type="file" wire:model="photo">

    <!-- Progress Bar -->
    <div x-show="isUploading">
        <progress max="100" x-bind:value="progress"></progress>
    </div>
</div>

JavaScriptアップロードAPI

サードパーティのファイルアップロードライブラリと統合するには、多くの場合、単純な<input type="file">タグよりも細かく調整したコントロールが必要になります。

このような場合にそなえ、Livewireは専用のJavaScript関数を公開してます。

これらの関数はJavaScriptコンポーネントオブジェクトに存在し、便利なBladeディレクティブ@thisを使用してアクセスできます。これまでに@thisを見かけたことがない場合は、ここで詳細を読むことができます。

<script>
    let file = document.querySelector('input[type="file"]').files[0]

    // ファイルのアップロード
    @this.upload('photo', file, (uploadedFilename) => {
        // 成功時のコールバック
    }, () => {
        // エラー時のコールバック
    }, (event) => {
        // 進捗のコールバック
        // event.detail.progressはアップロード状況として1から100を含む
    })

    // 複数のファイルをアップロードします。
    @this.uploadMultiple('photos', [file], successCallback, errorCallback, progressCallback)

    // 複数のアップロード済みファイルから単一のファイルを削除します
    @this.removeUpload('photos', uploadedFilename, successCallback)
</script>

設定

開発者がバリデートまたは保存する前に、すべてのファイルアップロードを一時的に保存するため、Livewireは、すべてのファイルアップロードのデフォルトの処理を想定しています。

グローバルなバリデーション

デフォルトでは、Livewireは以降のルールですべての一時ファイルのアップロードを検証します: file | max:12288(12MB未満のファイルである必要があります)。

これをカスタマイズしたい場合は、config/livewire.phpの中で、一時ファイルアップロード全部で実行するバリデーションルールを性格に設定してください。

return [
    ...
    'temporary_file_upload' => [
        ...
        'rules' => 'file|mimes:png,jpg,pdf|max:102400', // (100MB max, and only pngs, jpegs, and pdfs.)
        ...
    ],
];

グローバルミドルウェア

一時ファイルアップロードエンドポイントには、デフォルトでスロットルミドルウェアがあります。次の設定変数を使用して、このエンドポイントが使用するミドルウェアを正確にカスタマイズできます。

return [
    ...
    'temporary_file_upload' => [
        ...
        'middleware' => 'throttle:5,1', // 1分間に5アップロードまで許可
    ],
];

一時的なアップロードディレクトリ

一時ファイルは指定したディスクのlivewire-tmp/ディレクトリにアップロードされます。これは、以降の設定キーを使用してカスタマイズできます。

return [
    ...
    'temporary_file_upload' => [
        ...
        'directory' => 'tmp',
    ],
];

ドキュメント章別ページ

ヘッダー項目移動

注目:アイコン:ページ内リンク設置(リンクがないヘッダーへの移動では、リンクがある以前のヘッダーのハッシュをURLへ付加します。

移動

クリックで即時移動します。

言語
バージョン

設定

適用ボタンクリック後に、全項目まとめて適用されます。

カラーテーマ
和文指定 Pagination
和文指定 Scaffold
Largeスクリーン表示幅
インデント
本文フォント
コードフォント
フォント適用確認

フォントの指定フィールドから、フォーカスが外れると、当ブロックの内容に反映されます。EnglishのDisplayもPreviewしてください。

フォント設定時、表示に不具合が出た場合、当サイトのクッキーを削除してください。

バックスラッシュを含むインライン\Code\Blockの例です。

以下はコードブロックの例です。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * ユーザに関連する電話レコードを取得
     */
    public function phone()
    {
        return $this->hasOne('App\Phone');
    }
}

設定を保存する前に、表示が乱れないか必ず確認してください。CSSによるフォントファミリー指定の知識がない場合は、フォントを変更しないほうが良いでしょう。

キーボード・ショートカット

オープン操作

PDC

ページ(章)移動の左オフキャンバスオープン

HA

ヘッダー移動モーダルオープン

MS

移動/設定の右オフキャンバスオープン

ヘッダー移動

T

最初のヘッダーへ移動

E

最後のヘッダーへ移動

NJ

次ヘッダー(H2〜H4)へ移動

BK

前ヘッダー(H2〜H4)へ移動

その他

?

このヘルプページ表示
閉じる