FormData オブジェクトを使うと、フォームの入力内容を手軽に収集して fetch で非同期送信できます。ファイルを含む送信に特に強く、画面遷移なしのスムーズなアップロードを実装できます。この記事では基本の使い方から、メソッド一覧・送信時の注意点・ファイルアップロードまで実務目線で解説します。
- フォームからの生成と中身の確認(
console.logで見えない理由) append/set/get/getAll/has/deleteObject.fromEntriesでのオブジェクト化とJSON送信- fetch送信で
Content-Typeを手動指定してはいけない理由 - 複数ファイルのアップロードと進捗表示(XHR)
new FormData(form) に渡すだけで全フィールドを収集でき、fetch(url, { method: "POST", body: formData }) でそのまま送れます。Content-Type は自動設定されるので手動で指定しないのが鉄則です。ファイルが無いなら URLSearchParams やJSON送信も選べます。FormDataとは
FormData はフォームの内容をキーと値のペアとして保持するオブジェクトです。テキストだけでなくファイルも扱えるため、画像やドキュメントのアップロードでよく使われます。
基本:フォームから生成して中身を確認する
フォーム要素を渡すと、name 属性を持つ入力がすべて自動で収集されます。
<form id="my-form"> <input type="text" name="username" value="Taro"> <input type="email" name="email" value="taro@example.com"> <input type="file" name="avatar"> </form>
const form = document.getElementById("my-form");
const formData = new FormData(form);
// console.log(formData) では中身が見えないので entries() で確認
for (const [key, value] of formData.entries()) {
console.log(key, value);
}
値の追加・取得・操作(append / set / get / delete)
手動で値を足したり書き換えたりできます。append は同じキーでも複数追加、set は上書き(既存を消して1つに)という違いが重要です。
const data = new FormData();
data.append("tag", "js"); // 追加
data.append("tag", "web"); // 同じキーで複数追加できる
data.set("tag", "css"); // set は上書き(既存の tag を全部消して1つに)
data.get("tag"); // "css"(最初の値を1つ返す)
data.getAll("tag"); // ["css"](同じキーの全値を配列で)
data.has("email"); // false(キーがあるか)
data.delete("tag"); // 削除
// 数値は文字列に変換される
data.append("age", 25); // "25" として保存される
オブジェクトに変換する(Object.fromEntries)
デバッグやJSON送信のために、Object.fromEntries でプレーンなオブジェクトへ変換できます。
const formData = new FormData(form);
const obj = Object.fromEntries(formData);
console.log(obj); // { username: "Taro", email: "taro@example.com", ... }
// 注意: 同じ name が複数あると最後の値だけになる(複数は getAll を使う)
Ajaxで送信する(fetch)
FormData はそのまま fetch の body に渡せます。ファイルを含む場合も自動で multipart/form-data として送信されます。
const form = document.getElementById("my-form");
form.addEventListener("submit", (e) => {
e.preventDefault();
const formData = new FormData(form);
fetch("/submit.php", {
method: "POST",
body: formData, // Content-Type は自動設定される
})
.then((res) => res.text())
.then((result) => console.log("成功:", result))
.catch((error) => console.error("エラー:", error));
});
Content-Type を手動で指定してはいけないmultipart/form-data には、データの区切りを表すboundary(境界文字列)が必要で、ブラウザが自動生成します。headers: { "Content-Type": "multipart/form-data" } と自分で指定すると boundary が欠落してサーバーがデータを解析できず壊れます。FormDataを送るときは Content-Type を書かないのが正解です。fetch 全般のエラーハンドリングはfetch API の使い方、非同期処理の基礎はPromiseとasync/awaitの使い方を参照してください。送信前にデータを加工する
送信直前に値を追加・変換できます。タイムスタンプやトークンの付与などに便利です。
form.addEventListener("submit", (e) => {
e.preventDefault();
const formData = new FormData(form);
formData.append("timestamp", Date.now()); // 自動で値を追加
fetch("/submit.php", { method: "POST", body: formData });
});
ファイルが無いなら URLSearchParams / JSON も選べる
ファイルを含まないテキストだけのフォームなら、URLSearchParams(application/x-www-form-urlencoded)やJSON送信のほうが軽量で扱いやすいことがあります。
// urlencoded(ファイル不可・テキストのみ)
const params = new URLSearchParams(new FormData(form));
fetch("/submit.php", { method: "POST", body: params });
// JSON で送る場合は明示的に変換する
fetch("/api", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(Object.fromEntries(new FormData(form))),
});
ファイルアップロード:複数ファイルと進捗表示
複数ファイルを送るには multiple 属性のファイル入力をループして追加します。PHP側で配列として受け取れるよう、キーは files[] にします。
const fileInput = document.querySelector('input[type="file"][multiple]');
const formData = new FormData();
for (const file of fileInput.files) {
formData.append("files[]", file); // PHP では $_FILES["files"] に配列で届く
}
fetch はアップロード進捗を取得できません。進捗バーを表示したい場合は XMLHttpRequest の upload.progress イベントを使います。画像のドラッグ&ドロップ+プレビューは画像をドラッグ&ドロップで読み込む方法も参考になります。const xhr = new XMLHttpRequest();
xhr.upload.addEventListener("progress", (e) => {
if (e.lengthComputable) {
const percent = Math.round((e.loaded / e.total) * 100);
console.log(`アップロード: ${percent}%`);
}
});
xhr.open("POST", "/upload.php");
xhr.send(new FormData(form));
サーバー側での受け取り例(PHP)
サーバーでは通常のフォーム送信と同じく $_POST / $_FILES で受け取れます。ファイルは検証してから保存します。
<?php
$username = $_POST["username"] ?? "";
$email = $_POST["email"] ?? "";
$file = $_FILES["avatar"] ?? null;
// アップロード成功時のみ保存(拡張子・サイズ検証は別途必須)
if ($file && $file["error"] === UPLOAD_ERR_OK) {
move_uploaded_file($file["tmp_name"], "uploads/" . basename($file["name"]));
}
echo "名前: {$username}, メール: {$email}";
PHP側のファイルアップロード処理(検証・リネーム・保存先振り分け)はファイルのアップロード機能を実装する方法に詳しくまとめています。送信ボタンのローディングUIや二重送信防止はフォーム送信のローディング実装を参照してください。
よくある質問(FAQ)
fetch で非同期送信する場面に最適です。new FormData(formElement) で name 付きの全フィールドをまとめて収集できます。fetch のヘッダーで Content-Type を手動指定していませんか。FormDataでは boundary を含む Content-Type をブラウザが自動生成するため、自分で指定すると壊れます。Content-Type は書かないでください。console.log(formData) では中身が見えません。for (const [k, v] of formData.entries()) でループするか、Object.fromEntries(formData) でオブジェクト化して確認します。append は同じキーでも複数追加でき、set は既存の値を消して1つに上書きします。同じキーの全値を取り出すには getAll を使います。fetch はアップロード進捗を取得できないため、XMLHttpRequest の upload に progress イベントを設定します。e.loaded / e.total でパーセントを計算できます。まとめ
- 生成:
new FormData(form)で全フィールドを収集 - 操作:
append(複数追加)とset(上書き)を使い分け - 送信:
fetchのbodyに渡す。Content-Typeは手動指定しない - 代替:ファイル無しなら
URLSearchParams/ JSON も選べる - 進捗:アップロード進捗は
XMLHttpRequestで取得
FormDataの仕組みと Content-Type の注意点を押さえれば、ファイル送信を含むフォームを安全・簡単に実装できます。

