この章では、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)– 指定された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を持つすべてのフィールドを削除してから、新しいフィールドを追加することです。そのため、そのような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つの特異点を挙げておきましょう。
setメソッドは同じ名前のフィールドを削除しますが、appendは削除しません。それが唯一の違いです。- ファイルを送信するには、3つの引数を持つ構文が必要です。最後の引数はファイル名で、通常は
<input type="file">のユーザーファイルシステムから取得されます。
他のメソッドは次のとおりです。
formData.delete(name)formData.get(name)formData.has(name)
以上です!
コメント
<code>タグを使用し、複数行の場合は<pre>タグで囲み、10行を超える場合はサンドボックス(plnkr、jsbin、codepen…)を使用してください。