FormData オブジェクトの使用

FormData オブジェクトは、フェッチまたは XMLHttpRequest API を使用して送信するためのキーと値のペアの集合をコンパイルできます。本来はフォームデータの送信に使用することを想定していましたが、キーのついたデータを伝送するためにフォームとは独立して使用することもできます。伝送されるデータは、フォームのエンコードタイプが multipart/form-data に設定されている場合に、submit() メソッドで送信する際に使用するデータと同じ形式です。

ゼロから FormData オブジェクトを作成する

以下のように FormData オブジェクトはあなた自身で作成でき、インスタンス化したら append() メソッドを呼び出すことでフィールドに付加します。

js
const send = document.querySelector("#send");

send.addEventListener("click", async () => {
  const formData = new FormData();
  formData.append("username", "Groucho");
  formData.append("accountnum", 123456);

  // file 型の <input> 要素
  const avatar = document.querySelector("#avatar");
  formData.append("avatar", avatar.files[0]);

  // JavaScript のファイル風オブジェクト
  const content = '<q id="a"><span id="b">hey!</span></q>';
  const blob = new Blob([content], { type: "text/xml" });
  formData.append("webmasterfile", blob);

  const response = await fetch("http://example.org/post", {
    method: "POST",
    body: formData,
  });
  console.log(await response.json());
});

メモ: "avatar" および "webmasterfile" フィールドはどちらも、ファイルを含んでいます。フィールド "accountnum" に与えた数値は FormData.append() メソッドにより直ちに文字列へ変換されます(フィールドの値として BlobFile、文字列のいずれかを取ることができます。値が Blob でも File でもない場合は、文字列に変換されます)。

この例では、 "username", "accountnum", "avatar", "webmasterfile" というフィールドの値を含む FormData インスタンスを構築し、 fetch() を使用してフォームのデータを送信します。 "webmasterfile" フィールドは Blob です。 Blob オブジェクトは、不変的な生データのファイルのようなオブジェクトを表します。 Blob は、必ずしも JavaScript に適した形式ではないデータを表します。 File インターフェースは Blob をベースにしており、 Blob の機能を継承し、ユーザーのシステム上のファイルをサポートするように拡張されています。 Blob を作成するには、 Blob() コンストラクターを呼び出します。

HTML フォームから FormData オブジェクトを取り出す

既存の <form> のデータを含む FormData オブジェクトを構築するには、 FormData オブジェクトの作成時にその form 要素を指定します。

メモ: FormDataname 属性を使用する入力フィールドのみを使用します。

js
const formData = new FormData(someFormElement);

例:

js
const send = document.querySelector("#send");

send.addEventListener("click", async () => {
  // <form> 要素
  const userInfo = document.querySelector("#user-info");
  const formData = new FormData(userInfo);

  const response = await fetch("http://example.org/post", {
    method: "POST",
    body: formData,
  });
  console.log(await response.json());
});

以下のように、FormData オブジェクトをフォームより取得してから送信するまでの間に、追加のデータを付加することもできます。

js
const send = document.querySelector("#send");

send.addEventListener("click", async () => {
  const userInfo = document.querySelector("#user-info");
  const formData = new FormData(userInfo);
  formData.append("serialnumber", 12345);

  const response = await fetch("http://example.org/post", {
    method: "POST",
    body: formData,
  });
  console.log(await response.json());
});

これにより、必ずしもユーザーが編集可能である必要がない追加情報を含めるために、送信前にフォームデータを拡張することができます。

FormData オブジェクトを使用したファイルの送信

FormData を使用してファイルを送信することもできます。type が "file" である <input> 要素を、<form> に含めます。

html
<form enctype="multipart/form-data" method="post" name="fileinfo" id="fileinfo">
  <p>
    <label
      >Your email address:
      <input
        type="email"
        autocomplete="on"
        name="userid"
        placeholder="email"
        required
        size="32"
        maxlength="64" />
    </label>
  </p>
  <p>
    <label
      >Custom file label:
      <input type="text" name="filelabel" size="12" maxlength="32" />
    </label>
  </p>
  <p>
    <label
      >File to stash:
      <input type="file" name="file" required />
    </label>
  </p>
  <p>
    <input type="submit" value="Stash the file!" />
  </p>
</form>

そして、以下のようなコードを使用して送信できます。

js
const form = document.querySelector("#fileinfo");

form.addEventListener("submit", async (event) => {
  const formData = new FormData(form);

  formData.append("CustomField", "This is some extra data");

  const response = await fetch("stash.php", {
    method: "POST",
    body: formData,
  });
  event.preventDefault();
});

メモ: フォームへの参照を渡した場合は、 open() の呼び出しで指定したリクエストメソッドよりもフォームで指定したメソッドを優先します。

警告: FormData を使用して、XMLHttpRequest またはフェッチ API を使用して、 multipart/form-data の Content-Type で POST リクエストを送信する場合(ファイルや Blob をサーバーにアップロードする場合など)、リクエストの Content-Type ヘッダーを明示的に設定しないでください。そうすると、ブラウザーがリクエスト本文のフォームフィールドの区切りに使用する境界の表現で Content-Type ヘッダーを設定することができなくなります。

以下のように、直接 FileBlobFormData オブジェクトへ追加することもできます。

js
data.append("myfile", myBlob, "filename.txt");

append() メソッドを使用する際は、オプションの第 3 引数を使用して、Content-Disposition ヘッダーに含めるファイル名を渡すことができます。これはサーバーへ送信されます。ファイル名を指定しない(あるいは引数がサポートされない)場合は、 "blob" という名前が使用されます。

formdata イベントの使用

FormData オブジェクトよりも新しくプラットフォームに追加されたのが formdata イベントです。これは、フォームのデータを表すエントリーのリストが作成された後に HTMLFormElement オブジェクトで発行されます。このイベントは、フォームが送信されたときに発行されますが、 FormData() コンストラクターが呼び出されたときにも発行されます。

これにより、 FormData オブジェクトを formdata イベントの発行を受けてすばやく取得することができるようになり、自分でまとめる必要がなくなります。

例えば、 JavaScript では次のようにフォームを参照できます。

js
const formElem = document.querySelector("form");

submit イベントのハンドラーでは、 preventDefault を使用して既定のフォーム送信を停止してから、 FormData コンストラクターを呼び出して formdata イベントを発行させます。

js
formElem.addEventListener("submit", (e) => {
  // フォーム送信時に既定の動作を抑止
  e.preventDefault();

  // FormData オブジェクトを作成し、 formdata イベントを発行
  new FormData(formElem);
});

formdata イベントが発行されると、 FormData オブジェクトに FormDataEvent.formData を使ってアクセスし、必要な処理を行うことができます(以下では、 XMLHttpRequest を使ってサーバーに送信しています)。

js
formElem.addEventListener("formdata", (e) => {
  console.log("formdata fired");

  // イベントオブジェクトから形式データを取得します。
  const data = e.formData;
  for (const value of data.values()) {
    console.log(value);
  }

  // fetch() でデータを送信
  fetch("/formHandler", {
    method: "POST",
    body: data,
  });
});

分かりましたか

FormData オブジェクトは、無効になっているフィールドや無効になっているフィールドセットのデータを含めることはできません。

関連情報