このチャプターは HTML フォームの送信について説明します: ファイル有無や追加のフィールドなど。FormData オブジェクトはそれらの場合に役立ちます。
コンストラクタは次の通りです:
let formData = new FormData([form]);
HTML form
要素が提供されている場合、自動的にそのフィールドを捉えます。ご想像のとおり、 FormData
はフォームデータを保存したり送信したりするためのオブジェクトです。
FormData
に関して特別なことは、fetch
のようなネットワークメソッドが、本体(ボディ)として FormData
オブジェクトを受け入れることができるという点です。エンコードされ、Content-Type: form/multipart
で送信されます。なので、サーバ側から見ると、通常のフォーム送信のように見えます。
シンプルなフォームの送信
最初に、シンプルなフォームを送信してみましょう。
ご覧の通り、ほぼ1行でです。
<form id="formElem">
<input type="text" name="name" value="John">
<input type="text" name="surname" value="Smith">
</form>
<script>
(async () => {
let response = await fetch('/article/fetch-basics/post/user', {
method: 'POST',
body: new FormData(formElem)
});
let result = await response.json();
alert(result.message);
})();
</script>
ここでは、サーバーはフォームを使用したPOST要求を受け入れ、 “User saved” と返信します。
FormData メソッド
次のメソッドを使って FormData
のフィールドを変更することができます:
formData.append(name, value)
– 指定されたname
とvalue
のフォームフィールドを追加します。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
のフィールドをすべて削除し、その後新しいフィールドを追加します。なので、set
をした場合、name
のフィールドが1つであることを確認してみてください。:
formData.set(name, value)
,formData.set(name, blob, fileName)
.
また、for..of
ループを使用して、formData フィールドを反復することもできます:
let formData = new FormData();
formData.append('key1', 'value1');
formData.append('key2', 'value2');
// key/value ペアをリストします
for(let [name, value] of formData) {
alert(`${name} = ${value}`); // key1=value1, then key2=value2
}
ファイルを含むフォームを送信する
フォームは常に Content-Type: form/multipart
として送信され、このエンコーディングはファイルを送信することが可能です。そのため、通常のフォーム送信と同様に、 <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
を送信します(e.g 画像)。fetch
パラメータの body
に直接指定することができます。
しかし、実際には画像を個別に送信するのではなく、フォームの一部として、“name” や他のメタデータのような追加のフィールドと一緒に送信するのが便利なことが多いです。
また、通常サーバは生のバリナリデータよりもマルチパートエンコード形式を受け入れるのに適しています。
この例は、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">
があり、訪問者がファイルシステムから 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つの特徴があります:
set
メソッドは同じ名前のフィールドを削除します。append
は削除しません。- ファイルを送信するには、3つの引数の構文が必要になります。最後の引数はファイル名であり、
<input type="file">
の場合、通常はユーザのファイルシステムからとられます。
他のメソッドは次のとおりです:
formData.delete(name)
formData.get(name)
formData.has(name)
以上です!