In previous tutorial, we've shown you how to upload files and upload multiple files. So far, we've focused on the file part of multipart requests. In this tutorial, we'll concentrate on the data that goes along with the request, for example description string(s).
If you don't feel ready for such an advanced topic yet, catch up with our extensive library of Retrofit topics:
Retrofit Series Overview
- Getting Started and Creating an Android Client
- Basics of API Description
- Creating a Sustainable Android Client
- URL Handling, Resolution and Parsing
- How to Change API Base Url at Runtime
- Multiple Server Environments (Develop, Staging, Production)
- Share OkHttp Client and Converters between Retrofit Instances
- Upgrade Guide from 1.9
- Beyond Android: Retrofit for Java Projects
- How to use OkHttp 3 with Retrofit 1
- Synchronous and Asynchronous Requests
- Send Objects in Request Body
- Add Custom Request Header
- Manage Request Headers in OkHttp Interceptor
- Dynamic Request Headers with @HeaderMap
- Multiple Query Parameters of Same Name
- Optional Query Parameters
- Send Data Form-Urlencoded
- Send Data Form-Urlencoded Using FieldMap
- How to Add Query Parameters to Every Request
- Add Multiple Query Parameter With QueryMap
- How to Use Dynamic Urls for Requests
- Constant, Default and Logic Values for POST and PUT Requests
- Cancel Requests
- Reuse and Analyze Requests
- Optional Path Parameters
- How to Send Plain Text Request Body
- Customize Network Timeouts
- How to Trust Unsafe SSL certificates (Self-signed, Expired)
- Dynamic Endpoint-Dependent Interceptor Actions
- How to Update Objects on the Server (PUT vs. PATCH)
- How to Delete Objects on the Server
- Introduction to (Multiple) Converters
- Adding & Customizing the Gson Converter
- Implementing Custom Converters
- How to Integrate XML Converter
- Access Mapped Objects and Raw Response Payload
- Supporting JSON and XML Responses Concurrently
- Handling of Empty Server Responses with Custom Converter
- Send JSON Requests and Receive XML Responses (or vice versa)
- Unwrapping Envelope Responses with Custom Converter
- Wrapping Requests in Envelope with Custom Converter
- Define a Custom Response Converter
Multiple Parts with @PartMap
Multipart requests are often used for forms with an additional file. For example, we've utilized it in the past for a feedback form, which also allows the user to upload a photo.
If you just need to pass a single or two descriptions with a file, you can just declare it as a @Part
in your service declaration:
public interface FileUploadService {
// previous code for single description
@Multipart
@POST("upload")
Call<ResponseBody> uploadFile(
@Part("description") RequestBody description,
@Part MultipartBody.Part file);
}
This works great for small use cases, but if you need to send more than a handful of properties, it gets quite messy, especially if not all of them are always set.
Retrofit offers an easy solution, which makes the uploads quite customizable: @PartMap
. @PartMap
is an additional annotation for a request parameter, which allows us to specify how many and which parts we send during runtime. This can very helpful if your form is very long, but only a few of those input field values are actually send. Instead of declaring an interface method with 20 or more parameters you can use a single @PartMap
. Let's see this in action!
First of all, we need to create a new interface method with the @PartMap
annotation:
public interface FileUploadService {
// declare a description explicitly
// would need to declare
@Multipart
@POST("upload")
Call<ResponseBody> uploadFile(
@Part("description") RequestBody description,
@Part MultipartBody.Part file);
@Multipart
@POST("upload")
Call<ResponseBody> uploadFileWithPartMap(
@PartMap() Map<String, RequestBody> partMap,
@Part MultipartBody.Part file);
}
It's important that you use a Map<String, RequestBody>
implementation as a parameter type for the @PartMap
part of the request. As we've explained above, this allows you to send a runtime-dependent list of data along with your file. The brackets after the PartMap
are optional. You need to use them, if you want to specify the encoding, similar to the content encoding in FieldMaps.
The second part is filling the Map with data. In a previous tutorial, we've introduced two helper methods to create a RequestBody
for a String variable and file variable:
@NonNull
private MultipartBody.Part prepareFilePart(String partName, Uri fileUri) {
// https://github.com/iPaulPro/aFileChooser/blob/master/aFileChooser/src/com/ipaulpro/afilechooser/utils/FileUtils.java
// use the FileUtils to get the actual file by uri
File file = FileUtils.getFile(this, fileUri);
// create RequestBody instance from file
RequestBody requestFile =
RequestBody.create(
MediaType.parse(getContentResolver().getType(fileUri)),
file
);
// MultipartBody.Part is used to send also the actual file name
return MultipartBody.Part.createFormData(partName, file.getName(), requestFile);
Finally, let's use this method and view the entire code from creating the Retrofit service, to filling the request with data and enqueuing the request:
Uri fileUri = ... // from a file chooser or a camera intent
// create upload service client
FileUploadService service =
ServiceGenerator.createService(FileUploadService.class);
// create part for file (photo, video, ...)
MultipartBody.Part body = prepareFilePart("photo", fileUri);
// create a map of data to pass along
RequestBody description = createPartFromString("hello, this is description speaking");
RequestBody place = createPartFromString("Magdeburg");
RequestBody time = createPartFromString("2016");
HashMap<String, RequestBody> map = new HashMap<>();
map.put("description", description);
map.put("place", place);
map.put("time", time);
// finally, execute the request
Call<ResponseBody> call = service.uploadFileWithPartMap(map, body);
call.enqueue(...);
Of course, depending on your use case you've to fill in a bit of logic. If you've a similar scenario to our feedback form, you could go through the list of EditText
s and only add the content of non-empty ones to your multipart request.
Summary
In this tutorial, we've shown you the trick of the PartMap
annotation. Retrofit 2 makes it easy to counter overblowingly long method declarations of multipart requests by offering to send multiple parts with @PartMap
. We hope you've learned how to send a map of data with your request. If you've any questions, let us know in the comments below.