The previous posts guided you through various use cases of Retrofit and showed you opportunities to enhance the app with Retrofit’s built-in functionality. This post will show you how to upload files to a backend server using the second major release of Retrofit, namely Retrofit 2.

The content of this post is based on Retrofit 2.0.0-beta4. We’ll update this post if future releases of Retrofit change the actual handling and interface definition process!


Retrofit Series Overview

  1. Getting Started and Create an Android Client
  2. Basic Authentication on Android
  3. Token Authentication on Android
  4. OAuth on Android
  5. Multiple Query Parameters of Same Name
  6. Synchronous and Asynchronous Requests
  7. Send Objects in Request Body
  8. Define a Custom Response Converter
  9. Add Custom Request Header
  10. Optional Query Parameters
  11. How to Integrate XML Converter
  12. Using the Log Level to Debug Requests
  13. How to Upload Files to Server
  14. Series Round-Up
  15. Upgrade Guide from 1.9
  16. How to Upload Files to Server
  17. Log Requests and Responses
  18. Hawk Authentication on Android
  19. Simple Error Handling
  20. How to use OkHttp 3 with Retrofit 1
  21. Send Data Form-Urlencoded
  22. Manage Request Headers in OkHttp Interceptor
  23. How to Add Query Parameters to Every Request
  24. Add Multiple Query Parameter With QueryMap
  25. How to Use Dynamic Urls for Requests
  26. Url Handling, Resolution and Parsing
  27. Constant, Default and Logic Values for POST and PUT Requests
  28. How to Download Files from Server
  29. Cancel Requests
  30. Reuse and Analyze Requests
  31. How to Change API Base Url at Runtime

File Upload with Retrofit 1.x

We’ve already published a blog post on how to upload files using Retrofit 1.x. If you’re using Retrofit 1, please follow the linked guide.

Using Retrofit 2? This guide is for you and please read on :)

Upload Files With Retrofit 2

This post is intentionally separated from the already published post on how to upload files with Retrofit v1, because the internal changes from Retrofit 1 to Retrofit 2 are profound and you need to understand the way Retrofit 2 handles file uploads.

Before we dive deeper into the file upload topic with Retrofit 2, let’s shortly recap the previously used functionality in v1. Retrofit 1 used a class called TypedFile for file uploads to a server. This class has been removed from Retrofit 2. Further, Retrofit 2 now leverages the OkHttp library for any network operation and as a result OkHttp’s classes for use cases like file uploads.

Using Retrofit 2, you need to use OkHttp’s RequestBody class and encapsulate your file into a request body. Let’s have a look at the interface definition for file uploads.

public interface FileUploadService {      @Multipart    @POST("/upload")    Call<String> upload(            @Part("myfile\"; filename=\"image.png\" ") RequestBody file,            @Part("description") RequestBody description);}

You’re probably having a closer look at the content of the file’s @Part annotation. Actually, there is an existing bug in OkHttp which requires this hacky workaround to get file uploads working using Retrofit 2.

Let me explain each part of the definition above and we’re starting with the annotation for file. The first part myfileis the name of the file which gets send to the server. Additionally, we need to hard-code the filename so that your backend will recognize that there is a file as payload for further processing. The description is just a string value wrapped within a RequestBody instance. The description part doesn’t require any workaround to be sent correctly as part of the payload.

Android Client Code

We’ve defined the necessary interface for Retrofit and now we can move on and touch the Android’s part to upload a file. We’ll use the ServiceGenerator class which generates a service client. We’ve introduced theServiceGenerator class within the getting started with Retrofit post earlier within this series.

FileUploadService service =          ServiceGenerator.createService(FileUploadService.class);String descriptionString = "hello, this is description speaking";  RequestBody description =      RequestBody.create(MediaType.parse("multipart/form-data"), descriptionString);File file = new File("path/to/your/file");  RequestBody requestBody =          RequestBody.create(MediaType.parse("multipart/form-data"), file);Call<String> call = service.upload(requestBody, description);  call.enqueue(new Callback<String>() {      @Override    public void onResponse(Call<String> call, Response<String> response) {        Log.v("Upload", "success");    }    @Override    public void onFailure(Call<String> call, Throwable t) {        Log.e("Upload", t.getMessage());    }});

The snippet above just shows you the code to initialize the payload (file and description) and how to use the file upload service. As already mentioned, the RequestBody class is from OkHttp and used for file uploads. Its.create() method requires two parameters: first, the media type which we parse from multipart/form-data and second, the actual file. Then, we can pass the created request body as a parameter to the service interface and let Retrofit (in teamwork with OkHttp) handle the actual upload.

Remember the Content-Type

Please keep an eye on Retrofit’s content type. If you intercept the underlying OkHttp client and change the content type to application/json, your server might have issues with the deserialization process. Make sure you’re not defining the header indicating you’re sending JSON data, but multipart/form-data.

Exemplary Hapi Server for File Uploads

If you already have your backend project, you can lean on the example code below. We use a simple hapi server with a POST route available at /upload. Additionally, we tell hapi to don’t parse the incoming request, because we use a Node.js library called multiparty for the payload parsing.

Within the callback of multiparty’s parsing function, we’re logging each field to show its output.

method: 'POST',  path: '/upload',  config: {      payload: {        maxBytes: 209715200,        output: 'stream',        parse: false    },    handler: function(request, reply) {        var multiparty = require('multiparty');        var form = new multiparty.Form();        form.parse(request.payload, function(err, fields, files) {            console.log(err);            console.log(fields);            console.log(files);            return reply(util.inspect({fields: fields, files: files}));        });    }}

Android client expects a return type of String, we’re sending the received information as response. Of course your response will and should look different :)

Below you can see the output of a successful request and payload parsing. The first null is the err object. Afterwards, you can see the fields which is only the description as part of the request. And last but not least, the file is available within the files field. Here you see our previously defined names on client side. image is passed as the original name and the actual field name is myfile. For further processing, access the uploaded image at path’s location.

Server Log for Parsed Payload

null  { description: [ '"hello, this is description speaking"' ] }{ myfile:   [ { fieldName: 'myfile',       originalFilename: 'image.png" ',       path: '/var/folders/rq/q_m4_21j3lqf1lw48fqttx_80000gn/T/ttzVoNPfBxMMirec1tJsnrd2.png',       headers: [Object],       size: 82745 } ] }

Outlook

File uploads are an essential feature within up-to-date apps and even though file uploads are kind of hacky with Retrofit 2, please keep in mind that the second major release is still in beta. We’re sure that this will be fixed until the final 2.0 release and everyone will be happy!

What to expect within the next post on Retrofit? Next week we’ll all about how to get back logging within Retrofit 2. Stay tuned, it will be a good shot!