Commons HTTPClientのMultipart postでハマった件
Commons HTTPClient 3.1でファイルアップロードするクライアントを書いていて、ハマった。
参考にしたのは次の2サイト。
- http://634.ayumu-baby.com/commons/oss_jakartacommons_httpclient_multipart.html
- multipart post problem - Segal, Jeffrey - org.apache.hc.httpclient-users - MarkMail
…しかし、これらにあるサンプルコード通りに実装するとうまく動かない。*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は設定していない。やはり一次情報に当たらないとダメだな。