2021年6月22日

FormData

この章では、HTMLフォームの送信について説明します。ファイルの有無、追加フィールドなどについてです。

FormDataオブジェクトは、その際に役立ちます。ご想像のとおり、HTMLフォームデータを表すオブジェクトです。

コンストラクタは次のとおりです。

let formData = new FormData([form]);

HTMLのform要素が提供されている場合、そのフィールドは自動的にキャプチャされます。

FormDataの特別な点は、fetchなどのネットワークメソッドが、FormDataオブジェクトを本文として受け入れることができることです。エンコードされ、Content-Type: multipart/form-dataで送信されます。

サーバーの観点からは、通常のフォーム送信のように見えます。

単純なフォームの送信

まずは単純なフォームを送信してみましょう。

ご覧のとおり、ほぼ1行で記述できます。

<form id="formElem">
  <input type="text" name="name" value="John">
  <input type="text" name="surname" value="Smith">
  <input type="submit">
</form>

<script>
  formElem.onsubmit = async (e) => {
    e.preventDefault();

    let response = await fetch('/article/formdata/post/user', {
      method: 'POST',
      body: new FormData(formElem)
    });

    let result = await response.json();

    alert(result.message);
  };
</script>

この例では、サーバーコードは私たちの範囲外であるため、提示されていません。サーバーはPOSTリクエストを受け取り、「ユーザーが保存されました」と返信します。

FormDataのメソッド

FormDataのフィールドは、以下のメソッドで変更できます。

  • formData.append(name, value) – 指定されたnamevalueでフォームフィールドを追加します。
  • formData.append(name, blob, fileName)<input type="file">であるかのようにフィールドを追加します。3番目の引数fileNameは、ユーザーのファイルシステムのファイル名であるかのように、ファイル名(フォームフィールド名ではありません)を設定します。
  • formData.delete(name) – 指定されたnameのフィールドを削除します。
  • formData.get(name) – 指定されたnameのフィールドの値を取得します。
  • formData.has(name) – 指定されたnameのフィールドが存在する場合、trueを返し、そうでない場合はfalseを返します。

フォームは技術的には同じnameを持つ複数のフィールドを持つことが許可されているため、appendを複数回呼び出すと、同じ名前のフィールドが追加されます。

appendと同じ構文を持つsetメソッドもあります。違いは、.setは指定されたnameを持つすべてのフィールドを削除してから、新しいフィールドを追加することです。そのため、そのようなnameを持つフィールドは1つだけになり、残りはappendと同じです。

  • formData.set(name, value),
  • formData.set(name, blob, fileName).

また、for..ofループを使用してformDataフィールドを反復処理することもできます。

let formData = new FormData();
formData.append('key1', 'value1');
formData.append('key2', 'value2');

// List key/value pairs
for(let [name, value] of formData) {
  alert(`${name} = ${value}`); // key1 = value1, then key2 = value2
}

ファイル付きフォームの送信

フォームは常にContent-Type: multipart/form-dataとして送信されます。このエンコーディングにより、ファイルを送信できます。そのため、<input type="file">フィールドも通常のフォーム送信と同様に送信されます。

そのようなフォームの例を次に示します。

<form id="formElem">
  <input type="text" name="firstName" value="John">
  Picture: <input type="file" name="picture" accept="image/*">
  <input type="submit">
</form>

<script>
  formElem.onsubmit = async (e) => {
    e.preventDefault();

    let response = await fetch('/article/formdata/post/user-avatar', {
      method: 'POST',
      body: new FormData(formElem)
    });

    let result = await response.json();

    alert(result.message);
  };
</script>

Blobデータ付きフォームの送信

Fetchの章で見たように、動的に生成されたバイナリデータ(例:画像)をBlobとして送信するのは簡単です。 fetchパラメータbodyとして直接渡すことができます。

ただし、実際には、画像を個別に送信するのではなく、「名前」やその他のメタデータなどの追加フィールドとともにフォームの一部として送信すると便利な場合があります。

また、サーバーは通常、生のバイナリデータではなく、multipartエンコードされたフォームを受け入れる方が適しています。

この例では、FormDataを使用して、<canvas>からの画像を他のいくつかのフィールドとともにフォームとして送信します。

<body style="margin:0">
  <canvas id="canvasElem" width="100" height="80" style="border:1px solid"></canvas>

  <input type="button" value="Submit" onclick="submit()">

  <script>
    canvasElem.onmousemove = function(e) {
      let ctx = canvasElem.getContext('2d');
      ctx.lineTo(e.clientX, e.clientY);
      ctx.stroke();
    };

    async function submit() {
      let imageBlob = await new Promise(resolve => canvasElem.toBlob(resolve, 'image/png'));

      let formData = new FormData();
      formData.append("firstName", "John");
      formData.append("image", imageBlob, "image.png");

      let response = await fetch('/article/formdata/post/image-form', {
        method: 'POST',
        body: formData
      });
      let result = await response.json();
      alert(result.message);
    }

  </script>
</body>

画像Blobがどのように追加されるかに注目してください。

formData.append("image", imageBlob, "image.png");

これは、フォームに<input type="file" name="image">があり、訪問者がファイルシステムからデータimageBlob(2番目の引数)を持つ"image.png"(3番目の引数)という名前のファイルを送信した場合と同じです。

サーバーは、通常のフォーム送信であるかのように、フォームデータとファイルを読み取ります。

まとめ

FormDataオブジェクトは、HTMLフォームをキャプチャし、fetchまたは別のネットワークメソッドを使用して送信するために使用されます。

HTMLフォームからnew FormData(form)を作成するか、フォームなしでオブジェクトを作成し、メソッドを使用してフィールドを追加できます。

  • formData.append(name, value)
  • formData.append(name, blob, fileName)
  • formData.set(name, value)
  • formData.set(name, blob, fileName)

ここで2つの特異点を挙げておきましょう。

  1. setメソッドは同じ名前のフィールドを削除しますが、appendは削除しません。それが唯一の違いです。
  2. ファイルを送信するには、3つの引数を持つ構文が必要です。最後の引数はファイル名で、通常は<input type="file">のユーザーファイルシステムから取得されます。

他のメソッドは次のとおりです。

  • formData.delete(name)
  • formData.get(name)
  • formData.has(name)

以上です!

チュートリアルマップ

コメント

コメントする前にこれを読んでください…
  • 改善のための提案がある場合は、コメントする代わりに、GitHubのissueまたはプルリクエストを送信してください。
  • 記事の内容が理解できない場合は、詳しく説明してください。
  • 短いコードを挿入するには、<code>タグを使用し、複数行の場合は<pre>タグで囲み、10行を超える場合はサンドボックス(plnkrjsbincodepen…)を使用してください。