Commons HTTPClientのMultipart postでハマった件

Commons HTTPClient 3.1でファイルアップロードするクライアントを書いていて、ハマった。

参考にしたのは次の2サイト。

…しかし、これらにあるサンプルコード通りに実装するとうまく動かない。*1
まず、サンプルに忠実にこんな感じに書いた。

HttpClient client = new HttpClient();
PostMethod post = new PostMethod("http://localhost:8080/myapp/post");

File file = new File("/path/to/file");
        
Part[] parts = new Part[] { new FilePart(file.getName(), file) };
post.setRequestEntity(new MultipartRequestEntity(parts, post.getParams()));
post.setRequestHeader("Content-Type", "multipart/form-data");

client.executeMethod(post);

これを、Commons Fileupload 1.2で実装したサーバで受け取ろうとすると、例外をはく。

org.apache.commons.fileupload.FileUploadException: the request was rejected because no multipart boundary was found

問題点は、自分でContent-Typeを設定しているところ。

Part[] parts = new Part[] { new FilePart(path, new File(path) };
post.setRequestEntity(new MultipartRequestEntity(parts, post.getParams()));

// コイツ!!
post.setRequestHeader("Content-Type", "multipart/form-data");

client.executeMethod(post);

Multipart postでは、Content-Typeヘッダにboundaryが設定されていなければならない。例えば、こんな感じ。

Content-Type=multipart/form-data; boundary=zWHtGdHXhVRonRDwrKO8J0qucmDwGfloby

これはHttpClient(MultipartRequestEntity)が勝手に作ってくれる。しかし、サンプル通りに後から自分でContent-Typeを設定すると、boundaryの定義を消してしまうことになる。このため、サーバに蹴られる。

だから、次のように書かなければならないのだ。

HttpClient client = new HttpClient();
PostMethod post = new PostMethod("http://localhost:8080/myapp/post");

File file = new File("/path/to/file");
        
Part[] parts = new Part[] { new FilePart(file.getName(), file) };
post.setRequestEntity(new MultipartRequestEntity(parts, post.getParams()));

// 自分で設定しない!
// post.setRequestHeader("Content-Type", "multipart/form-data");

client.executeMethod(post);

ちなみに、MultipartRequestEntityのJavadocにあるサンプルでは、当然のようにContent-Typeは設定していない。やはり一次情報に当たらないとダメだな。

*1:バージョンが違うだけかもしれないが。