Internet Explorer のサポート廃止に伴い、Laravelアプリケーションのファイルアップロード処理を焼き直してみました。
View(フロントエンド)の修正
<form method="POST" action="{{ route('upload') }}">
@csrf
<input type="file" name="csvfile">
:
HTML5のFormDataオブジェクトをJavaScriptで作成し、サーバに送信します。
FormDataオブジェクトにセットするファイルは、コンストラクタから既存のform要素で渡せますが、今回はinput要素をappend()
メソッドで直接追加するため、form要素は削除します。
<input type="file" name="csvfile">
対して、JavaScriptのコードは増えます。
アロー関数やPromiseチェーンなど、ES6(ECMAScript2015)で書いているのでpolyfillしないとIEでは動きませんが、IEはサポートしないと利用者に断っているので問題ありません。Promiseは非同期処理のコールバック関数を簡単に記述できる便利な仕組みですが、それだけで記事になるくらいの情報量なのでここでは説明しません。
// このメソッドは clickやdropイベントで起動する
// filesはinput要素
function handleFileUpload(files) {
const fd = new FormData();
fd.append('csvfile', files[0]); //先頭のファイルのみ追加
fetch('{{ route("upload") }}', {
method: 'POST',
body: fd,
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}' //Laravelの場合これが必要
},
})
.then(res => {
if (!res.ok) { //通信失敗
throw new Error(`${res.status}: ${res.statusText}`);
}
return res.json(); //JSONを取り出して次のチェーンへ
})
.then(json => {
if (json.result) { //正常
$('#guide').text(`${json.category}のデータを${json.count}件登録しました`); //$()はjQuery
} else { //アプリケーションレベルのエラー
$('#guide').text(json.message); //$()はjQuery
}
})
.catch(error => alert(error.message))
};
FormDataオブジェクトは、従来のXMLHttpRequest
(XHR)やajax
の非同期通信でもサーバに送信できますが、タスクの順列・並行処理の指定が困難なので、Promiseを返却できるモダンなAPIのfetch
を使います。
fetch
が返すResponseオブジェクトには便利なok
プロパティがあり、HTTPステータスコードが200番台だとtrueがセットされます。
さて、Laravelフレームワークにも当然、クロスサイトリクエストフォージェリ(CSRF)攻撃を弾く仕組みがありますので、非同期通信のPOSTでも正しいCSRFトークンが必要です。リクエストヘッダのX-CSRF-TOKEN
にcsrf_token()
を指定してあげれば良いです。
Controller(バックエンド)の修正
クライアントからファイルを受け取る処理は変わりません。
返却値をJSON形式に書き替えるだけです。
//正常時
return view('aaa')->with('category', 'XXX')->with('count', $count);
//異常時
return view('aaa')->with('message', 'ファイルが不正です');
Laravelのコントローラでは、配列を返却すると、ヘッダ含めてJSON形式に変換してくれます。
//正常時
return ['result' => true, 'category' => 'XXX', 'count' => $count ];
//異常時
return ['result' => false, 'message' => 'ファイルが不正です'];
次回は、気が向いたらフロント側の処理を掘り下げて記事にしようと思います。