iimon TECH BLOG

iimonエンジニアが得られた経験や知識を共有して世の中をイイモンにしていくためのブログです

今更学ぶFormData〜multipart/form-dataを添えて〜

1. はじめに

こんにちはiimonでフロントエンドエンジニアを担当している「みよちゃん」です!

業務中にAPIにリクエストを送信することが多々あり、その際にFormDataを使用しているのですが、お恥ずかしながらその正体をちゃんと理解しないまま使用していました。

このままではいけない!と思い今回復習を兼ねて記事にまとめていきます!

2. そもそもFormDataって?

FormDataインターフェイスは、フォームフィールドとそれらの値から表現されるkeyとvalueのセットを簡単に構築することができる手段です。FormDataはfetchXMLHttpRequest.sendnovigator.sendBeaconのメソッドを用いて送信することが可能です。form要素のエンコーディング型をmultipart/form-dataに設定した場合と同じ形式となります。

3. multipart/form-data

通常のフォーム送信ではapplication/x-www-form-urlencodedが使用されますが、これはテキストデータのみを扱うことができます。しかし、multipart/form-dataを使用することで、テキストデータだけでなくファイルデータを送信することができます。また、boundaryという境界線を使用することで複数のデータを一度に送ることができます。

multipart/form-dataはRFCの7578に定義されています

a. 実際のデータを見てみよう

ここでは実際にmultipart/form-dataがどのに記述されるか見てみましょう!

boundaryは指定しない場合自動で設定されますが、今回は—iimonincをboundaryとして実際のデータを見てみようと思います!以下のデータは名前、メールアドレス、プロフィール画像を含んだデータになります!

Header部分

Post / upload HTTP/1.1
Host: iimon.com
Content-Type: multipart/form-data; boundary=iimoninc

body部分

--iimoninc -①
Contant-Disposition: form-data; name="user-name"
Content-Type: text/plain -②

iimonkun

--iimoninc
Content-Disposition: form-data; name="email"
-③
iimonkun@iimon.co.jp

--iimoninc
Content-Disposition: form-data; name="profile_image"; filename="iimon.jpeg" -④
Content-Type: image/jpeg

.......

コード内に挿入している①-④に関して解説したいと思います!

①boundary

boundaryに指定しているiimoninc--が区切り文字としてデータとデータの間に記載されています。

②③Content-Type

Content-Typeの後にデータの形式が何かを記載しています。

Content-Typeを指定しないとdefaultでtext/plainが設定されるようですが、明示的に指定することが推奨されています。

④filename

ファイルをアップロードする際にはfilenameを指定する必要があります。自分はfilenameの指定が必要なことを知らずに、実務中躓いたことがあります。。

b. multipart/form-dataのデメリット

一度のform送信で複数のデータを送れて、さらにファイルまで送れるなんてなんて便利なんでしょうか!!

しかし、超有能なmultipart/form-dataにも使用するデメリットが存在します。。

  1. パフォーマンス

    これはみなさん何となくお察しかと思いますが、各パートごとにヘッダーが追加されるため、送信するデータのサイズが大きくなります。特に小さなファイルを送る場合にはヘッダーの大きさが顕著になります

  2. 複雑さ

    こちらも各パートごとにヘッダーが追加されることに起因しますが、ヘッダーが複数存在することによりサーバー側の処理が複雑になってしまいます

  3. セキュリティ

    ファイルのアップロードが可能になため、悪意のあるファイルのアップロードによるセキュリティリスクが増加します。

4. FormDataの使い方

FormDataのprototypeを見てみると以下のようになっています。

おまり大きい声では言えませんが、自分はコンソールに出力してみるまで知らないメソッドがいくつかありました。。

a. FormDataのメソッド

  • append(key, value, fileName)
    • FormDataにkey, value, (fileName)を追加します
  • set(key, value, fileName)
    • append同様に値を追加しますが、重複したnameがある場合はフィールドを削除します
  • delete(name)
    • keyがnameのフィールドを削除します
  • get(name)
    • keyがnameの値を返します
  • has(name)
    • FormDataオブジェクトがnameがkeyの値を持っているかをbool値で返します。

b. FormDataの作成

FormDataの使い方はとてもシンプルです。まずFormDataを作成します。

const fd = new FormData()

フォームデータにkeyとvalueの組み合わせを設定してみましょう!

const profile: {
    name:string;
    email:string;
    imageBlob:blob;
} 
= {
    name: 'iimonkun',
    email: 'iimonkun@iimon.co.jp',
    profile_image: {} // blobデータ
};

fd.append('name',name);
fd.apend('email',email);
fd.append('profile_image',imageBlob,'profile.jpeg')

実際のデータを見た際にもお話ししましたが、ファイルを追加する場合はファイル名を設定する必要があるのでお忘れなく!!

また、FormDataをFormタグから作成する場合は以下のようになります。

<form id="formElm">
    <input type="text" name="name">
    <input type="text" name="email">
    <input type="file" name="profile_image" accept="image/*">

</form>
<script>
   const formElm = document.getElementById('formElm')
   const fd = new FormData(formElm)
</script>

ちなみにFormDataにはフィールドを反復することができるメソッドがいくつかあります。

以下にいくつか例を挙げますので、デバッグ時に是非ご活用ください!

// forEachを使用した例
fd.forEach((val, key) => {
    console.log(key, val)
}

// entriesを使用した例
for(const [key, val] of fd.entries()) {
    console.log(key, val)
}

5.おわりに

今回はFormDataについてまとめていみましたが、途中からmultipart/form-dataの記事になっていることに気が付いたので、サブタイトルをつけました。

フロントエンドからリクエストを送る際、どのような形式でデータを送るかはapiやサーバーの仕様に依存するところが大きいですが、使用しいている技術がどういったものかをある程度理解していることは重要なことですね。

しっかり理解せずに使用している技術を可能な限り0にしたいな~と思う今日この頃です。。

弊社では現在エンジニアを募集しております!

この記事を読んで少しでも弊社に興味を持っていただけましたら是非カジュアル面談でお話ししましょう!!お話しできるのを楽しみにしています!! Wantedly / Green

参考記事

https://datatracker.ietf.org/doc/html/rfc7578

https://developer.mozilla.org/ja/docs/Web/API/FormData

https://qiita.com/sivertigo/items/14957200af5ad953d63b