Documents

Laravel + vue.jsのファイルアップロードの諸々


Laravel8 + vue.jsでファイルをアップロードする方法です。Laravelだけで非同期でやる方法からvue.jsのRouterを使ってLaravelとAPI通信する方法まで様々ですが、基本的なところから順番に説明します。PHPを使った非同期の方法はそこそこ普通にやれば良い感じです。

非同期のファイルアップロード

CSRFトークン

注意点はCSRFトークンを渡して上げることです。これはbladeの中で@csrf{{ csrf_field() }}をformタグの中に入れてあげるとCSRFトークンがinputのhiddenで勝手に設定してくれて、それをRequestで受けることができます。_token で必ず受けるのでフレムワークだなぁといった感じです。

テキストデータ

input, select, textarea, で普通にアップロードすれば基本的にはLaravel側でRequestで受けることができます。テキストの場合は特に楽でnameの値をオブジェクトメソッドで受けてあげればよいだけです。

$request->INPUT_NAME

ファイルデータ

少しばかり特殊なのはファイルアップロードの受け方で、ファイルだけは、

$request->file('INPUT_NAME')

という受け方になります。dd()でデバックするとオブジェクトとして表示されます。アスキーデータと違って様々なメタデータが格納されていることがわかります。それぞれの個別の値はメソッドチェーンで取得することができます。

$request->file("INPUT_NAME")->getClientOriginalName(); //元のファイル名
$request->file('INPUT_NAME')->getMimeType(), // ファイルのmimeタイプを取得(ファイルデータ)
$request->file('INPUT_NAME')->getClientMimeType(), // ファイルのmimeタイプを取得(ファイル拡張子)
$request->file('INPUT_NAME')->getClientOriginalExtension(), // 拡張子
$request->file('INPUT_NAME')->getSize(), // ファイルサイズ取得

ちなみにgetClientSize()は新しいLaravelではもう使えなくなっているメソッドです。
ここまでが非同期でのFormアクションの注意点といったところです。

データの保存

テキストデータに関してはDBに保存するなり、ログを取るなり適当に保存できるのですが、面倒なのはファイル保存です。Laravelでは保存ストレージが用意されてあって比較的容易に保存できるようにはなっています。
デフォルトではstorage/app/public/*以下に保存するような設定になっていて、このディレクトリを公開ディレクトリのpublic内にシンボリックリンクを作るという仕組みでWEBサーバーに公開しています。つまりこればstorage以下の他のディレクトリ内に格納すると内部参照はできるけど、公開はされないということができるわけです。また安レンタルサーバーではシンボリックリンクが作れないのでpublic/image/*っぽい公開ディレクトリにファイルを保存しないといけないようなことも時々生じます。ここらの定義はapp/config/filesystems.phpに書かれてあって、言ってみればパスの定義をしているだけっつう話になってます。デフォルトではS3に保存できるようになっていたりします。なので定義をするとGCPのstorageにも保存できるし、どこか別のリモートストレージにも保存できるし、SFTPサーバーとかも指定できます。
ここではLaravelの公開ディレクトリ以下に任意のディレクトリを作成してファイルを保存してみましょう。

'disks' => [
...
    'public_files' => [
        'driver' => 'local',
        'root'   => public_path('files'),
    ],
...

こんな感じで定義を追加しておきましょう。これはpublic/files/*に保存するという指示になります。さてこの場所に実際にファイルを保存する方法は、

use Illuminate\Support\Facades\Storage;
...
Storage::disk('public_files')->put('a/1', $request->file("INPUT_NAME"));

こんな感じになります。publich/files/a/1/xxxxxxxx.jpgみたいな感じで保存されます。しかし、これだとファイル名が任意の乱数文字列になってしまうので、ファイルを連番にしたいとか、プレフィックスを付けたいとか、あるいはサムネイルの大中小とオリジナルの4つを保存したいとか細やかな要件を満たすことができません。なので、Laravelではいろいろなファイルの保存方法が用意されています。
この場合はリクエストオブジェクトから直接操作します。

ファイル名を指定する

ややこしいのですが、上記と全然構文が違うんですよね。storeAs()メソッドは、argsの1にパス、argsの2にファイル名、argsの3に指定の保存先ディスクを指定します。

$path = $request->file('f_file1')->storeAs(
  'a/1',
  'foobar.jpg',
  ['disk' => 'public_files'],
);

これで比較的自由にファイル名を操作できます。通常はファイル名と拡張子を分解したり、GDで全部pngにしてから保存したり、いろいろできると思います。
ここらあたりの詳しいことは、公式サイトの
Laravel 8.x ファイルストレージ
にも詳しく載ってます。



2022.05.25