Retrofit 2 — How to Upload a Dynamic Amount of Files to Server

We've already shown you in a previous tutorial how to upload files and how to upload multiple files to a server. In this tutorial, we'll show you a way of uploading multiple files with one request without knowing how many files it'll be. This is useful when the user can dynamically select files.

If you want to catch up with one of our plenty Retrofit topics, check the outline of Retrofit tutorials:

Retrofit Series Overview

Upload Files With Retrofit 2

If this is your first uploading files with Retrofit tutorial, you should visit our uploading files with Retrofit and uploading multiple files tutorials.

In the previous tutorials, we've used various upload options in the FileUploadService class:

public interface FileUploadService {  
    // previous code for single file uploads
    @Multipart
    @POST("upload")
    Call<ResponseBody> uploadFile(
            @Part("description") RequestBody description,
            @Part MultipartBody.Part file);

    // previous code for multiple files
    @Multipart
    @POST("upload")
    Call<ResponseBody> uploadMultipleFiles(
            @Part("description") RequestBody description,
            @Part MultipartBody.Part file1,
            @Part MultipartBody.Part file2);
}

The second option let's you upload multiple files, but you always have to specify in advance how many. This is difficult when your app doesn't have a fixed number of files and it can vary depending on the use case or user input.

Upload a Dynamic Amount of Files

The solution for this problem is to pass a List or Array of MultipartBody.Part objects. Retrofit and OkHttp will then build an appropriate multipart request with all files. Java arrays or lists allow you to freely add files as required.

Endpoint Declaration

You know the theory, so it's time to look at an example. As always, we'll start with describing the endpoint interface. Of course, this depends on your backend. Make sure your API can handle a random amount of files!

public interface FileUploadService {  
    @Multipart
    @POST("upload")
    Call<ResponseBody> uploadMultipleFilesDynamic(
            @Part("description") RequestBody description,
            @Part List<MultipartBody.Part> files);
}

In the previous examples we carried a description with every request. We'll keep it to show you how it would work, but of course it's only a single description for a lot of files. If you need to also send a dynamic amount of other information, you should check out our tutorial on @PartMap.

The second part of the implementation is using the new endpoint declaration and passing some files. We'll reuse the helper methods from our previous tutorials to simplify creating the necessary multiparts:

@NonNull
private RequestBody createPartFromString(String descriptionString) {  
    return RequestBody.create(
            okhttp3.MultipartBody.FORM, descriptionString);
}

@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, we'll put everything together to build the upload request:

Uri photoUri = ... // get it from a file chooser or a camera intent  
Uri videoUri = ... // get it from a file chooser or a camera intent  
// ... possibly many more file uris

// create list of file parts (photo, video, ...)
List<MultipartBody.Part> parts = new ArrayList<>();

// add dynamic amount
if (photoUri != null) {  
    parts.add(prepareFilePart("photo", photoUri));
}

if (videoUri != null) {  
    parts.add(prepareFilePart("video", videoUri));
}

// ... possibly add more parts here

// add the description part within the multipart request
RequestBody description = createPartFromString("hello, this is description speaking");

// create upload service client
FileUploadService service = ServiceGenerator.createService(FileUploadService.class);

// finally, execute the request
Call<ResponseBody> call = service.uploadMultipleFilesDynamic(description, parts);  
call.enqueue(...);  

All of the steps above should be fairly familiar to you. We simply create a part for each file and the description. After everything is put together, we can create a new Call object from our FileUploadService and execute the request as usual.

Summary

In this tutorial, you've learned how to upload a dynamic amount of files with Retrofit, which can be incredibly useful when your app lets the user chose the files.

If you've feedback or a question, let us know in the comments or on twitter @futurestud_io.

Make it rock & enjoy coding!


Explore the Library

Find interesting tutorials and solutions for your problems.