覚書です。
Contents
multipart/form-data(バイナリ)とbase64の比較、どっちがいいのか?
multipart/form-dataはデータ送信の構造を指し、Base64は画像データのエンコード方法を指します。したがって、multipartの文脈でBase64という言葉を使うことはある気がします。
Base64 エンコードの特徴
• テキスト化
Base64 はバイナリデータをテキスト形式に変換します。これにより、JSON のようなテキストベースのデータ構造に直接組み込むことができ、HTTPリクエストのボディとして扱いやすくなります。
• 互換性
既存の実装や API の仕様が base64 を前提としている場合、そのまま使うと既存のコードとの互換性が保たれます。これにより、変更による不具合やミスのリスクが少なくなります。
• オーバーヘッド
一方で、base64 エンコードすると元のデータサイズが約 33% 増加するというデメリットがあります。
multipart/form-data の特徴
• バイナリ送信
multipart/form-data は、ファイルなどのバイナリデータをそのまま分割して送信する形式です。エンコードのオーバーヘッドがなく、データサイズがそのままで済むため、大容量ファイルの送信時に有利です。
• 柔軟性
複数のフィールド(テキストデータやファイルデータ)を同時に送信できるので、複雑なフォームデータのやり取りに適しています。
• 実装の複雑性
ただし、multipart/form-data を扱うためには、リクエストの境界文字列やパートごとのヘッダーなど、細かい設定が必要になるため、実装がやや複雑になりがちです。また、API 側もこの形式での受信に対応していなければなりません。
Ensure the POST is a
https://developer.x.com/en/docs/x-api/v1/media/upload-media/api-reference/post-media-uploadmultipart/form-data
request. Either upload the raw binary (media
parameter) of the file, or its base64-encoded contents (media_data
parameter). Use raw binary when possible, because base64 encoding results in larger file sizes
X APIではbase64はサイズが大きくなると説明されています。
Webの口コミをみると、意見はわれるようです。
元々はBase64エンコードした文字列をJSONで送っていたけどそれだとパフォーマンスの問題があったので、multipart/form-dataで送るようにして対応した。Node.jsのmulterというライブラリを使おうとしたけどなぜか動かなかったのでbusboyというライブラリを使った。
— 小林一哉/Web系フリーランスエンジニア/Golang/DDD/AWS/二級FP技能士 (@k_kazuya_1981) March 6, 2025
もうマシンが十分なメモリ持っているから全部一気にパースしてしまった方がデータも扱いやすいって事なんすかね?
— Naoko Manchun Milky Naomi Tåkãnø🏊 (@honten) December 17, 2024
multipart/form-data(バイナリ)のサンプルコード
boundaryは乱数、PNGはbase64にします。
"use strict";
/*
* Minimal multipart/form-data sample code
* Now with a random boundary and base64 dummy data for the file.
*/
(async () => {
// 1) boundary文字列の用意
// この文字列は区切りとして機能し、フォームデータの開始・終了を分かりやすくするためのものです。
// 衝突しにくいようランダムに生成することも多いです。
const boundary = "----Boundary" + Math.random().toString(36).slice(2);
// 2) Prepare base64 data for the file (dummy)
// 実際には画像などをBase64化した文字列をここに入れてください。
const base64DummyData = "iVBORw0KGgoAAAANSUhEUgAAAAUA...";
// 3) Build request body
// boundaryを使って区切りを明示的に入れながら、テキストデータやファイルデータを組み立てます。
// \r\n は改行を意味しています(Windows系の改行コードですが、HTTPヘッダなどでは一般的に使われます)。
// boundaryによる区切りと、Content-Dispositionなどのヘッダ情報を手動で書いています。
const body =
`--${boundary}\r\n` +
`Content-Disposition: form-data; name="username"\r\n\r\n` +
`sampleUser\r\n` +
`--${boundary}\r\n` +
`Content-Disposition: form-data; name="avatar"; filename="test.png"\r\n` +
`Content-Type: image/png\r\n\r\n` +
base64DummyData + // Base64でエンコードされたダミー文字列
`\r\n` +
`--${boundary}--\r\n`;
// 4) Send POST request using Fetch API
// multipart/form-data; boundary=<boundary> を明示し、組み立てた body を送信します。
const response = await fetch("/upload", {
method: "POST",
headers: {
"Content-Type": `multipart/form-data; boundary=${boundary}`
},
body: body
});
// 5) Print out the result from server
console.log("Upload result:", await response.text());
})();
multipart/form-data で区切るための boundary(境界文字列)を設定しています。実際は好きな文字列でOKですが、衝突しにくいようランダムに生成することも多いです。
// 実行結果例
Math.random() // 0.7123456789
Math.random().toString(16) // "0.b67d3"
Math.random().toString(16).slice(2) // "b67d3"
Build request bodyのフォーマットはそういうものだと覚えるとよいでしょう。
- 文字列で手動作成する場合、以下のような書式を意識してください。
- –<boundary>\r\n で始まり、Content-Disposition: form-data; name=”フィールド名” やファイルの情報を記述します。
- データを入れたら改行し、次のフィールドに移ります。
- 最後は –<boundary>–\r\n としてフォームデータの終了を明示します。
- テキストデータとファイルをまとめて送れるのが multipart/form-data のメリットです。
- 実際のファイルバイナリを扱う場合は、File オブジェクトやBlob、またはArrayBufferなどを使ってバイナリデータを構築し、文字列連結ではなくUint8Arrayなどで連結する必要がある点に注意してください。
既存のコードをどう書き換えていけばいいのかという覚書です。
videoInitOption = {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + oauth2Service.getAccessToken(),
'Content-Type': 'application/json'
},
payload: JSON.stringify({
command: 'INIT',
media_type: 'video/mp4',
media_category: 'tweet_video',
total_bytes: videoFileSize
}),
muteHttpExceptions: true
};
このように書き換えます。
const boundary = '----WebKitFormBoundary' + Math.random().toString(16).slice(2);
const initPayload =
'--' + boundary + '\r\n' +
'Content-Disposition: form-data; name="command"\r\n\r\n' +
'INIT\r\n' +
'--' + boundary + '\r\n' +
'Content-Disposition: form-data; name="media_type"\r\n\r\n' +
'video/mp4\r\n' +
'--' + boundary + '\r\n' +
'Content-Disposition: form-data; name="media_category"\r\n\r\n' +
'tweet_video\r\n' +
'--' + boundary + '\r\n' +
'Content-Disposition: form-data; name="total_bytes"\r\n\r\n' +
videoFileSize + '\r\n' +
'--' + boundary + '--'; // Important to close boundary
videoInitOption = {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + oauth2Service.getAccessToken(),
'Content-Type': 'multipart/form-data; boundary=' + boundary
},
payload: initPayload,
muteHttpExceptions: true
};
GASとGoogleドライブのAPIと画像データ
- Google APIなしにGoogleドライブにアクセスすることはできるよう!同じGoogleのサービスだから特別にAPIなしにということでしょうか。アップロードやダウンロードができます。(ただし、Google Picker APIを使うためにはやはりGoogleドライブのAPIが必要で、できることは限定されています。ユーザーがGUI上でファイル選択の操作できない)
- Googleドライブを画像置き場として使った場合、Googleの画像Urlは拡張子がつかない形式です。そのあと、使おうとするときいろいろまずいようで一工夫必要そうです。
Googleドライブの画像を使うにあたり、3パターンほどやりくちがありそう!
https://drive.google.com/uc?export=view&id=YOUR_FILE_ID
というURLに変える方法!この方法は簡単ですが、Googleの正式仕様ということではないっぽい。- Blobデータを直接利用する。バイナリデータでエンコードをしなくてもいいけど、ちょっとややこい。
- Base64エンコードを利用する。Base64エンコードはよく使われますが、バイナリデータよりも画像が約1/3大きくなるため、データ量が増えます。
個人的にややこいBlobデータを使いました。どの方法もGoogle APIを使うことは回避できます。
コメント