Form使わない!送信後も画面更新させずに表示処理継続可能な実装方法

やあ子供たち。寒いけど元気にしているかい。
ファイルのアップロードが出来るページを作る際に、基本として紹介されているのが、

  1. Formを作成する。
  2. その中にinputボタン(ファイル選択)(type="file")を作成
  3. その中にinputボタン(送信)(type="submit")を作成

といった流れですが、上述の実装方法では以下のような問題があります。

  • ユーザーがsubmitボタンを押した途端に、画面がリフレッシュされてしまい、サーバーがレスポンスとして返してくる画面に切り替わってしまう                           
  • ファイルの送信後にサーバーとsocket.io 経由の送受信処理をしたいのだが、肝心なsocket.idも一緒に送りたいということがあるのにこの方法だと送れない(socket.io接続はインタラクティブなWebページがサーバを双方向通信する上での命綱です!)

といったことが、どうしても起きてしまいます。
しかしながら、場合によっては

  • ファイル送信後もページの表示や動きを停めたくない
  • ユーザーがフォーム入力した情報と一緒に何らかのシステム関連情報も送りたい。

といったケースもあるでしょう。
そういう場合に一つの選択肢となり得る、XMLHttpRequestを使った実装方法を紹介したいと思います。
Formは一切使わずに、以下のようなシンプルなHTMLを用意し、

    <span>Type some words to upload</span>
    <input type="text" id="edit1" value="some words..." />
    <br>
    <span>Select files to upload</span>
    <input type="file" id="fileSelector1" name="file1" value="Select..." />
    <br>
    <button id="submit" onclick="onClickedUpdateGo()"> UPLOAD GO </button>

上記の送信用ボタンのコールバックとして、以下のようなコードを置きます。

  function onClickedUpdateGo()
  {
      var req = new XMLHttpRequest();       
      req.open("POST", "/upload_post");
      var form = new FormData();
      {
        let text1 = document.getElementById("edit1").value;
        let file1 = document.getElementById("fileSelector1").files[0];
        form.append("key1", text1);
        form.append("key_file1", file1, file1.name);
      }
      req.send(form);  // formの内容を送信
  }

はい、実はもう、これだけなんですね。

少しだけ上記コードの解説ですが、動的に生成したXMLHttpRequestを使って、POST送信を行います。送信内容はFormDataであり、ユーザーが入力したテキストや、アップロード用に選択したファイルをその中に詰め込んでいます。一つ分かりにくいのが、file1変数でしょうか。これはblob型とされるものであり、それが具体的に何かを考える必要はなく、つまりこれがユーザーが選択したファイルを送信するために必要なとなる情報なのだと理解しましょう。file1.nameは、パスを含まないファイル名となっています。

はい、これでサーバーが上がっている状態で、上記HTMLを実行し、適当な文字列をテキストボックスに打ち込みファイルセレクタで任意のファイルを選択した後に、「送信」ボタンを押してみましょう。ファイルは問題なくアップロードされていながら、ページのリロードなど一切発生せず、socket.ioも問題なく受信し続けられることが確認できるかと思います。

以上、「勝手に画面リロードが始まらない、socket.io接続も維持され続け」ながら、ユーザーが入力したテキストや、ユーザーが指定したアップロードファイルを、サーバに送信するだけの処理を実装させることが実現できたので、ご紹介でした。