JavaでAPIにファイルを送る処理を作ろうとすると、よく出てくる言葉が multipart/form-data です。
ですが「POSTで送ればいいんじゃないの?」「boundaryってなに?」と、ここで止まってしまう人がとても多いです。
たとえば、次のようなAPIはよくあります。
こうしたAPIでは、文字データ(usernameなど)とファイルを一緒に送る必要があります。
そのときに使われるのが multipart/form-data という特別なPOST形式です。
この記事では、Java初心者の方でも理解できるように
を できるだけやさしく解説していきます。

「なんとなくコピペで動かす」ではなく、仕組みまでしっかり理解できるようになりますので、ぜひ最後まで読んでみてください。
multipart/form-dataとは?初心者でもわかる基本
ファイルアップロードで使われるPOST形式
multipart/form-dataは、ファイルをサーバーに送るときに使うPOST形式です。
普通のPOSTは、主に次のような文字データを送るために使われます。
username=taro
age=20
ですが、ファイルは少し特別です。
などは、文字ではなくバイナリデータだからです。
そのためHTTPでは、ファイル送信専用の形式として
multipart/form-data が用意されています。
イメージとしては、データをいくつかのパーツに分けて送る方式です。
普通のPOST送信との違い
普通のPOST送信は、データが1つのかたまりで送られます。
例:
username=taro&age=20
一方、multipart/form-dataでは、データをパーツごとに分けて送ります。
username
file
message
つまり
を 1つのリクエストの中にまとめて送れる仕組みです。
boundaryは「データの区切り線」
multipart/form-dataで大事なものが boundary(バウンダリ)です。
これは簡単に言うと、
データとデータの区切り線です。
たとえば、お弁当を想像してみてください。
お弁当には仕切りがありますよね。
[ごはん][唐揚げ][サラダ]
この「仕切り」の役目をするのが boundary です。
実際のHTTPリクエストは、こんなイメージになります。
--boundary
Content-Disposition: form-data; name="username"
taro
--boundary
Content-Disposition: form-data; name="file"; filename="sample.csv"
(ファイルデータ)
--boundary--
ポイントはこの3つです。
--boundaryでパーツ開始- ヘッダーを書く
- データを書く
そして最後に
--boundary--
で終わります。

この仕組みを理解しておくと、Javaでの実装がかなり理解しやすくなります。
Javaでファイルアップロードする流れ
Javaでmultipart/form-dataを送るときは、だいたい次の流れになります。
1つずつ見ていきましょう。
URLとPOSTリクエストを準備する
まずは、送信先のAPI URLを用意して、
POSTリクエストを作ります。
Javaではよく HttpURLConnection を使います。
流れはこんな感じです。
- URL作成
- 接続作成
- POST指定
イメージコードです。
URL url = new URL(apiUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
これで「POSTで送る準備」ができました。
Content-Typeをmultipart/form-dataにする
次に、HTTPヘッダーを設定します。
ここがとても重要です。
Content-Type: multipart/form-data
さらに boundary も指定します。
例:
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary123
Javaではこう書きます。
conn.setRequestProperty(
"Content-Type",
"multipart/form-data; boundary=" + boundary
);
この設定を忘れると、サーバーはファイル送信だと認識してくれません。
テキストとファイルを送る
次に OutputStream を使ってデータを書き込みます。
順番はこうです。
- boundaryを書く
- ヘッダーを書く
- データを書く
テキスト送信のイメージです。
--boundary
Content-Disposition: form-data; name="username"
taro
ファイルの場合はこうなります。
--boundary
Content-Disposition: form-data; name="file"; filename="sample.csv"
Content-Type: text/csv
(ファイルデータ)
終端データを送る
最後に、multipartの終了を送ります。
--boundary--
この「–」付きのboundaryが終端です。
これを書かないと、サーバーは
「まだデータが続くのかな?」
と判断してしまいます。
その結果
になることがあります。
Javaでmultipart/form-dataを送るサンプルコード
最小構成のサンプルコード
次は、実際に動く最小コードを紹介します。
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Files;
public class MultipartUpload {
public static void main(String[] args) throws Exception {
String boundary = "----JavaBoundary";
File file = new File("sample.txt");
URL url = new URL("https://example.com/upload");
HttpURLConnection conn =
(HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setRequestProperty(
"Content-Type",
"multipart/form-data; boundary=" + boundary
);
OutputStream output = conn.getOutputStream();
PrintWriter writer =
new PrintWriter(new OutputStreamWriter(output, "UTF-8"), true);
writer.append("--" + boundary).append("\r\n");
writer.append(
"Content-Disposition: form-data; name=\"file\"; filename=\"sample.txt\""
).append("\r\n");
writer.append("Content-Type: text/plain").append("\r\n");
writer.append("\r\n").flush();
Files.copy(file.toPath(), output);
output.flush();
writer.append("\r\n");
writer.append("--" + boundary + "--").append("\r\n");
writer.close();
int responseCode = conn.getResponseCode();
System.out.println(responseCode);
}
}
このコードだけでも、ファイルアップロードAPIに送信できます。
usernameとfileを一緒に送る例
よくあるパターンは
を一緒に送るケースです。
--boundary
Content-Disposition: form-data; name="username"
taro
--boundary
Content-Disposition: form-data; name="file"; filename="test.csv"
つまり
テキスト → ファイル
の順番でパーツを書けばOKです。
画像ファイルやCSVを送る例
Content-Typeを変更すれば、いろいろなファイルを送れます。
画像の場合:
Content-Type: image/png
CSVの場合:
Content-Type: text/csv
よくあるアップロード例は次のとおりです。
| ファイル | Content-Type |
|---|---|
| PNG | image/png |
| JPG | image/jpeg |
| CSV | text/csv |
| application/pdf |
コードのやさしい解説
boundaryの役割
boundaryは、データの区切り線です。
コードではここです。
String boundary = "----JavaBoundary";
そして実際の送信では
--boundary
として使います。
ヘッダーの意味
この行はとても重要です。
Content-Disposition: form-data
これは
「フォームデータですよ」
という意味です。
さらに
name="file"
で パラメータ名
filename="test.csv"
で ファイル名 を指定しています。
ファイル読み込み処理
ファイル送信の部分はここです。
Files.copy(file.toPath(), output);
これは
「ファイルの中身をそのままHTTPに書き込む」
処理です。
レスポンス確認
最後にレスポンスを確認します。
int responseCode = conn.getResponseCode();
成功なら多くの場合
200
が返ります。
よくあるエラーと原因
400 Bad Request
原因で多いのはこの2つです。
特に多いのが
--boundary--
を書いていないケースです。
415 Unsupported Media Type
このエラーは
Content-Type
が間違っている場合に起きます。
multipart送信なら必ず
multipart/form-data
を指定します。
ファイルが空になる問題
原因の多くは
です。
output.flush();
writer.close();
は忘れないようにしてください。
文字化けの原因
テキスト送信では
UTF-8
を使うようにしましょう。
new OutputStreamWriter(output, "UTF-8")
これを忘れると、日本語が壊れることがあります。
Apache HttpClientで簡単にアップロードする方法
ライブラリを使うメリット
実は、multipart/form-dataは
自分で書くとかなり面倒です。
そこでよく使われるのが Apache HttpClient です。
メリットはこちらです。
つまり ミスが減ります。
HttpClientサンプルコード
とてもシンプルです。
CloseableHttpClient client = HttpClients.createDefault();
HttpPost post = new HttpPost("https://example.com/upload");
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.addTextBody("username", "taro");
builder.addBinaryBody("file", new File("test.csv"));
post.setEntity(builder.build());
CloseableHttpResponse response = client.execute(post);
HttpURLConnectionよりも、かなりコードが短くなります。
うまくいかないときのチェックリスト
アップロードが失敗するときは、次を確認してください。
チェックポイントはこちらです。
特に多いのが改行コードミスです。
multipartでは
\r\n
を使う必要があります。
まとめ
今回は Javaでmultipart/form-dataを使ったファイルアップロード方法を解説しました。
ポイントを整理するとこちらです。
まずはこの記事の 最小サンプルコードを実行してみてください。
慣れてきたら、
などのライブラリを使うと、さらに簡単に実装できます。
ぜひ実際にコードを動かして、Javaのファイルアップロードを体験してみてください。
よくある質問
- Qmultipart/form-dataとは何ですか?
- A
multipart/form-dataとは、HTTPのPOSTリクエストでファイルとテキストを一緒に送るためのデータ形式です。
通常のPOST(application/x-www-form-urlencoded)は文字列データを送るための形式ですが、ファイルを送る場合はデータを複数のパートに分ける必要があります。
そのため multipart/form-data では、以下のようにデータを区切って送信します。
------boundary
Content-Disposition: form-data; name="username"taro
------boundary
Content-Disposition: form-data; name="file"; filename="sample.jpg"
Content-Type: image/jpeg(ファイルデータ)
------boundary--この boundary(境界線) によって、テキストとファイルのデータを区切っています。
- Q普通のPOST送信との違いは何ですか?
- A
通常のPOST送信では、Content-Typeは次の形式になります。
application/x-www-form-urlencoded
例えば次のようなデータです。
username=taro&age=20
しかしファイルを送る場合は、バイナリデータを扱う必要があるため、次の形式を使います。
multipart/form-data
この形式では、データを複数のブロック(パート)に分けて送信する仕組みになっています。
- Qboundaryとは何ですか?
- A
boundary(バウンダリ)とは、multipart/form-dataのデータを区切るための文字列です。
例:
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryABC123
この場合、リクエストボディの中では次のように使われます。
------WebKitFormBoundaryABC123
データ
------WebKitFormBoundaryABC123
データイメージとしては、お弁当の仕切りのようなものです。
この区切りがあることで、サーバーは「どこからどこまでが1つのデータか」を理解できます。
- QJavaでファイルアップロードするときによくあるエラーは?
- A
Javaのmultipart実装でよくあるエラーは次の4つです。
① 400 Bad Request
原因
- boundaryが一致していない
- 改行コード(CRLF)が不足
対策
\r\nを正しく入れる- Content-Typeのboundaryと本文のboundaryを一致させる
② 415 Unsupported Media Type
原因
- Content-Typeが間違っている
対策
Content-Type: multipart/form-data; boundary=XXXX
を設定する。
③ ファイルが空になる
原因
- OutputStreamに書き込めていない
- flush / closeしていない
④ 日本語が文字化けする
原因
- charset未指定
対策
Content-Type: text/plain; charset=UTF-8
- QHttpURLConnectionとApache HttpClientはどちらを使うべきですか?
- A
結論から言うと、実務ではApache HttpClientを使う方が簡単です。
理由は次の通りです。
項目 HttpURLConnection Apache HttpClient コード量 多い 少ない boundary処理 手動 自動 実装難易度 高い 低い 学習用 ◎ ○ 実務 △ ◎ HttpURLConnectionは仕組み理解には良いですが、
実際の開発では HttpClientのMultipartEntityBuilder を使うと簡単に実装できます。
