1
Help us understand the problem. What are the problem?

posted at

updated at

Laravelのファイルアップロード処理をFetchAPIで書き直した

Internet Explorer のサポート廃止に伴い、Laravelアプリケーションのファイルアップロード処理を焼き直してみました。

View(フロントエンド)の修正

修正前HTML
<form method="POST" action="{{ route('upload') }}">
  @csrf
  <input type="file" name="csvfile">
  :

HTML5のFormDataオブジェクトをJavaScriptで作成し、サーバに送信します。
FormDataオブジェクトにセットするファイルは、コンストラクタから既存のform要素で渡せますが、今回はinput要素をappend()メソッドで直接追加するため、form要素は削除します。

修正後HTML
<input type="file" name="csvfile">

対して、JavaScriptのコードは増えます。
アロー関数Promiseチェーンなど、ES6(ECMAScript2015)で書いているのでpolyfillしないとIEでは動きませんが、IEはサポートしないと利用者に断っているので問題ありません。Promiseは非同期処理のコールバック関数を簡単に記述できる便利な仕組みですが、それだけで記事になるくらいの情報量なのでここでは説明しません。

JavaScript(ES6)
// このメソッドは 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-TOKENcsrf_token()を指定してあげれば良いです。

Controller(バックエンド)の修正

クライアントからファイルを受け取る処理は変わりません。
返却値をJSON形式に書き替えるだけです。

修正前PHP
//正常時
return view('aaa')->with('category', 'XXX')->with('count', $count);

//異常時
return view('aaa')->with('message', 'ファイルが不正です');

Laravelのコントローラでは、配列を返却すると、ヘッダ含めてJSON形式に変換してくれます。

修正後PHP
//正常時
return ['result' => true, 'category' => 'XXX', 'count' => $count ];

//異常時
return ['result' => false, 'message' => 'ファイルが不正です'];

次回は、気が向いたらフロント側の処理を掘り下げて記事にしようと思います。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
1
Help us understand the problem. What are the problem?