【JavaScript】FormDataの使い方|Ajax送信・append/set・ファイルアップロード・進捗・Content-Typeの注意まで

JavaScript

FormData オブジェクトを使うと、フォームの入力内容を手軽に収集して fetch で非同期送信できます。ファイルを含む送信に特に強く、画面遷移なしのスムーズなアップロードを実装できます。この記事では基本の使い方から、メソッド一覧・送信時の注意点・ファイルアップロードまで実務目線で解説します。

この記事でわかること

  • フォームからの生成と中身の確認(console.logで見えない理由)
  • append / set / get / getAll / has / delete
  • Object.fromEntries でのオブジェクト化とJSON送信
  • fetch送信で Content-Type を手動指定してはいけない理由
  • 複数ファイルのアップロードと進捗表示(XHR)
結論:フォーム要素を new FormData(form) に渡すだけで全フィールドを収集でき、fetch(url, { method: "POST", body: formData }) でそのまま送れます。Content-Type は自動設定されるので手動で指定しないのが鉄則です。ファイルが無いなら URLSearchParams やJSON送信も選べます。
スポンサーリンク

FormDataとは

FormData はフォームの内容をキーと値のペアとして保持するオブジェクトです。テキストだけでなくファイルも扱えるため、画像やドキュメントのアップロードでよく使われます。

基本:フォームから生成して中身を確認する

フォーム要素を渡すと、name 属性を持つ入力がすべて自動で収集されます。

HTML
<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>
JavaScript:生成と中身の確認
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つに)という違いが重要です。

JavaScript:FormData の主なメソッド
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 でプレーンなオブジェクトへ変換できます。

JavaScript:オブジェクト化
const formData = new FormData(form);

const obj = Object.fromEntries(formData);
console.log(obj); // { username: "Taro", email: "taro@example.com", ... }

// 注意: 同じ name が複数あると最後の値だけになる(複数は getAll を使う)

Ajaxで送信する(fetch)

FormData はそのまま fetchbody に渡せます。ファイルを含む場合も自動で multipart/form-data として送信されます。

JavaScript:fetch で送信
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の使い方を参照してください。

送信前にデータを加工する

送信直前に値を追加・変換できます。タイムスタンプやトークンの付与などに便利です。

JavaScript:送信前に値を足す
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 も選べる

ファイルを含まないテキストだけのフォームなら、URLSearchParamsapplication/x-www-form-urlencoded)やJSON送信のほうが軽量で扱いやすいことがあります。

JavaScript:URLSearchParams / 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[] にします。

JavaScript:複数ファイルを追加
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"] に配列で届く
}
進捗バーには XMLHttpRequest を使う
fetchアップロード進捗を取得できません。進捗バーを表示したい場合は XMLHttpRequestupload.progress イベントを使います。画像のドラッグ&ドロップ+プレビューは画像をドラッグ&ドロップで読み込む方法も参考になります。
JavaScript:XHR でアップロード進捗を取得
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:受け取りと保存
<?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)

QFormDataはどんな場面で使いますか?
Aファイルを含むフォームを fetch で非同期送信する場面に最適です。new FormData(formElement)name 付きの全フィールドをまとめて収集できます。
Q送信したのにサーバーでデータが受け取れません。
Afetch のヘッダーで Content-Type を手動指定していませんか。FormDataでは boundary を含む Content-Type をブラウザが自動生成するため、自分で指定すると壊れます。Content-Type は書かないでください。
QFormDataの中身をデバッグするには?
Aconsole.log(formData) では中身が見えません。for (const [k, v] of formData.entries()) でループするか、Object.fromEntries(formData) でオブジェクト化して確認します。
Qappendとsetの違いは?
Aappend は同じキーでも複数追加でき、set既存の値を消して1つに上書きします。同じキーの全値を取り出すには getAll を使います。
Qアップロードの進捗バーを出したいです。
Afetch はアップロード進捗を取得できないため、XMLHttpRequestuploadprogress イベントを設定します。e.loaded / e.total でパーセントを計算できます。

まとめ

  • 生成new FormData(form) で全フィールドを収集
  • 操作append(複数追加)と set(上書き)を使い分け
  • 送信fetchbody に渡す。Content-Typeは手動指定しない
  • 代替:ファイル無しなら URLSearchParams / JSON も選べる
  • 進捗:アップロード進捗は XMLHttpRequest で取得

FormDataの仕組みと Content-Type の注意点を押さえれば、ファイル送信を含むフォームを安全・簡単に実装できます。