This is the last post in our Retrofit series. File upload functionality is often required for mobile apps. Before we start, let’s have an overview of the previously released posts.
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
Introduction
Most Android apps need some kind of upload functionality where you want to send your profile picture to the server or upload other files to share with your friends. However, this guide can be used for any kind of file and not just images.
File Upload with Retrofit 2.x
We’ve published an updated blog post on how to upload files using Retrofit 2.x
. If you’re using Retrofit 2, please follow the linked guide.
Upload Files with Retrofit 1
Retrofit provides the ability to perform Multipart requests. That enables the feature to upload files to a server. The @Multipart
annotation is required to perform those requests. The following code shows the method definition to upload a file to a given API endpoint.
public interface FileUploadService {
public static final String BASE_URL = "http://your.api/endpoint/base-url";
@Multipart
@POST("/upload")
void upload(@Part("myfile") TypedFile file,
@Part("description") String description,
Callback<String> cb);
}
This FileUploadService
interface is the client side API declaration. You’re going to request http://your.api/endpoint/base-url/upload
hooked with two parameters: a file myfile
and String description
.
To perform the actual request, you need to instantiate a RestAdapter
. Have a look at our Retrofit Getting Started Guide where we present a ServiceGenerator
class to create services for defined interfaces.
The ServiceGenerator
(described in the getting started post) creates and instantiates the required HTTP client for your service interface. In our case, we pass the FileUploadService
to the static method createService
and get a functional implementation to perform the upload
.
The following code snippet illustrates the usage of previously described methods.
FileUploadService service = ServiceGenerator.createService(FileUploadService.class, FileUploadService.BASE_URL);
TypedFile typedFile = new TypedFile("multipart/form-data", new File("path/to/your/file"));
String description = "hello, this is description speaking";
service.upload(typedFile, description, new Callback<String>() {
@Override
public void success(String s, Response response) {
Log.e("Upload", "success");
}
@Override
public void failure(RetrofitError error) {
Log.e("Upload", "error");
}
});
TypedFile
is a class from Retrofit representing a file with mime type. We use multipart/form-data
as mime type and create a file from given path.
That’s the upload part on Android side :)
Remember the Content-Type
Attention: keep an eye on the content type of the RestAdapter
. If you specify the header request.addHeader("Content-Type", "application/json");
via RequestInterceptor
, the request will probably fail at server side since your’re sending multipart data and not JSON.
Example hapi.js Server to Handle File Uploads
We’ve tested the upload functionality from Android to the backend with a hapi.js server. For that, we implemented a rudimentary functionality to just log the incoming data at server side. Multiparty was our module of choice for data parsing operations. Of course, you can use whatever you prefer.
The hapi.js route configuration for our file upload test.
server.route([
{
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);
});
}
}
}]);
We want to separate the topics of file upload with Retrofit on Android and the hapi.js server. That’s why we just leave the code snippet above without any additional comments. If you run into any problems or questions regarding the server test code, you can for sure just let us know in the comments or via twitter @futurestud_io.
Nevertheless, here the server log for a successful request.
Server Log for incoming data
null
{ description: [ 'hello, this is description speaking' ] }
{ myfile:
[ { fieldName: 'myfile',
originalFilename: 'IMG-20150311-WA0000.jpg',
path: '/var/folders/rq/q_m4_21j3lqf1lw48fqttx_80000gn/T/wcvFPnGewEPCm9IHM7ynAS3I.jpg',
headers: [Object],
size: 34287 } ] }
The first null
log is the err
object. Afterwards the description
field and finally myfile
.
Questions & Problems
Things went south? Just let us know via @futurestud_io or the comments below and we’ll help you get things working.