retrofitでマルチパートなものをPOSTする
retrofitはHTTPクライアントだけあって当然formの要素や画像のようなマルチパートなものもPOSTできるのだけど、その方法があんまり見つからなかったので覚書。見つからなかったというよりか、現状の最新安定バージョンの2.0でのPOSTの方法と過去のバージョン1.9での方法がかなり違う上に1.9での情報ばっかりがあふれていたので見つけにくかった…というだけだったのだけど。
概要
1.9ではTypedFileというクラスを使ってそれに画像なり何なりをいれてPOSTするのだけど、2.0ではRequestBodyにPOSTしたい要素を入れるようにする。 インターフェース部分は以下のように。@Multipartアノテーションと@Partアノテーションを使う。@Partアノテーションをつけた引数としてRequestBodyを指定する。
public interface FileUploadService { @Multipart @POST("/upload") Call<String> upload( @Part("file") RequestBody file ); }
で、使う場合は以下のようにする。RequestBodyのインスタンスを生成する時に、MediaTypeを指定する必要がある。
FileUploadService service = ServiceGenerator.createService(FileUploadService.class); File file = new File("path/to/your/file"); RequestBody requestBody = RequestBody.create(MediaType.parse("multipart/form-data"), file); Call<String> call = service.upload(requestBody); call.enqueue(new Callback<String>() { //... });
たくさんの要素をPOSTしたい
formと言っても要素は複数あるとか、画像1枚だけじゃなくて複数枚POSTしたいという場合はPartMapを使う。 @Partアノテーションでなく、@PartMapアノテーションを使う。そして引数はその名の通りMap。
public interface FileUploadService { @Multipart @POST("/upload") Call<String> upload( @PartMap Map<String, RequestBody> params ); }
FileUploadService service = ServiceGenerator.createService(FileUploadService.class); Map<String, RequestBody> requestParams = new HashMap<>(); File file1 = new File("path/to/your/file1"); RequestBody requestBody1 = RequestBody.create(MediaType.parse("multipart/form-data"), file1); File file1 = new File("path/to/your/file2"); RequestBody requestBody2 = RequestBody.create(MediaType.parse("multipart/form-data"), file2); requestParams.put("files[]", requestBody1); requestParams.put("files[]", requestBody2); Call<String> call = service.upload(requestParams); call.enqueue(new Callback<String>() { //... });
という感じで本当にMapライクに使えるので便利
@Bodyと併用は出来ない
以下のようなインターフェースを書いてたらエラーが出た。
public interface FileUploadService { @Multipart @POST("/upload") Call<String> upload( @Body ItemEntity item, @PartMap Map<String, RequestBody> params ); }
ItemEntityはjavaのオブジェクト<=>jsonの変換をしている。json<=>javaオブジェクトのコンバーターが絡む@Bodyとコンバートをしない@Partおよび@PartMapではうまくいかないみたい。なのでどっちかにする必要がある。画像をあげる必要があるなら@Partを素直に使ったほうが楽かもしれない。@Bodyだとfileはそのままjson変換されたらおかしくなるためbase64エンコードするとか色々面倒事があるので…
参考 :
Retrofit 2 — How to Upload Files to Server
How to Upload a File using Retrofit 2.0 · Issue #1063 · square/retrofit · GitHub