How to upload files from Android App to server

By , last updated November 6, 2019

In this article we will show how to program a solution for file uploading from your Android app to Java server in the background using OkHttp library.

We will create a service to upload files in the existing Android app created with Android Studio. The app code will be in Java. We will also provide server side code in Groovy that will receive the file on the server and save to an arbitrary location. Server code may easily be translated to Java if you are writing your server code in Java.

Send file to server

First, let’s create a service that will pack the log files from our Android app and send them to the server.

We will call it for FileUploadService and it will be an extension of an IntentService. You may wonder why we chose IntentService instead of AsyncTask. The main decision point is that AsyncTask is blocking the main UI thread while executing. We don’t need it. Our file upload will happen in the background without the user noticing, so we need something that will perform a task and not lock the app. The IntentService runs on a separate worker thread and is perfect for our task.

FileUploadService.java

public class FileUploadService extends IntentService {
    public FileUploadService() {
        super(FileUploadService.class.getSimpleName());
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        //send log file here
    }
}

Create request

We will use OkHttp library to send the request to server with our log file. The library is very easy to use. It doesn’t provide too much functionality though. It does provide silent retries in some cases, but will interrupt if your WiFi or internet connection goes down.

Let’s start creating the OkHttpClient object and prepare it to send the files to the server.

First, create the OkHttp client builder and set connection and read timeouts:

OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(30, TimeUnit.SECONDS);
builder.readTimeout(30, TimeUnit.SECONDS);

Next, find the file you want to send to the server. In case of log files, zip them together into one zip file using Zip4j:

File file = getLogFile();

Next step is to create a RequestBody. In this requestBody we will place the file and a String with a username. Remember, the app may be used by many users, så you will need to know whom are you pulling the logs from right now.

In order to send both the file and an arbitrary property or a list of arguments together in one server request, we will use the Multipart feature of the Okhttp library. We will create a MultipartBody object and place all our data as FormDataParts:

RequestBody requestBody = new MultipartBody.Builder()
                    .setType(MultipartBody.FORM)
                    .addFormDataPart("file", file.getName(),
                            RequestBody.create(MediaType.parse("text/csv"), file))
                    .addFormDataPart("username", username)
                    .build();

Next, create the request itself:

Request requestBuilder = new Request.Builder()
                    .header("Authorization", userToken)
                    .url(url)
                    .post(requestBody)
                    .build();

Limit retries

You may want to limit the number of retries that the OkHttp library makes in case of bad response from server.

Create the interceptor for your OkHttp client and set the retry number to something you like. We will set that number to 3 which means that if our client gets response code other that 2xx or 3xx it will retry the request 3 more times. Remember, the request will be sent in the same form as the original one unless you change it. It means that if you send a file and don’t get the success response, it will send the same file 3 more times. This may or may not be beneficial for your end goal.

Here is an example of the interceptor you may use:

builder.addInterceptor(new Interceptor() {
                @Override
                public Response intercept(Chain chain) throws IOException {
                    Request request = chain.request();

                    // try the request
                    Response response = chain.proceed(request);

                    int tryCount = 0;
                    int maxLimit = 3; //Set your max limit here

                    while (!response.isSuccessful() && tryCount < maxLimit) {
                        tryCount++;

                        // retry the request
                        response = chain.proceed(request);
                    }

                    // otherwise just pass the original response on
                    return response;
                }
            });

Send request

It’s time to send the request to the server.

OkHttpClient client = builder.build();

Response response = client.newCall(requestBuilder).execute();

Here is the whole code for how to send the file from your Android app to the Java or Groovy server:

public class FileUploadService.java extends IntentService {
    public FileUploadService() {
        super(FileUploadService.class.getSimpleName());
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        String username = "myName";
	String userToken = "some%token%data";

        try {
            String url = "http://serverurl/api/upload";

            OkHttpClient.Builder builder = new OkHttpClient.Builder();
            builder.connectTimeout(30, TimeUnit.SECONDS);
            builder.readTimeout(30, TimeUnit.SECONDS);

            File file = getLogFile();

            RequestBody requestBody = new MultipartBody.Builder()
                    .setType(MultipartBody.FORM)
                    .addFormDataPart("file", file.getName(),
                            RequestBody.create(MediaType.parse("text/csv"), file))
                    .addFormDataPart("username", username)
                    .build();

            Request requestBuilder = new Request.Builder()
                    .header("Authorization", userToken)
                    .url(url)
                    .post(requestBody)
                    .build();
            
            OkHttpClient client = builder.build();

            Response response = client.newCall(requestBuilder).execute();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

Receive file on server

In this section we will show how you can receive the file on the server using Java or Groovy. We will use Groovy, but it’s almost identical with how you would do it in Java.

The data from the app will be received by your API Controller and the params arguments will hold all the data.

In order to save the file on server you may use the Spring framework MultipartFile.transferTo() method. As we are sending the request in parts, we will need to receive it in parts as well.

def upload() {
	String username = params.username
	String path = createNewFilePath();

	File logFile = new File(path)
	
	params.file.transferTo(logFile)

    	render “Ok”
}

Errors

You may encounter some errors while receiving the file.

The ones we’ve seen are
IllegalAccessError occurred when processing request: [POST] /api/upload - parameters:...
tried to access class
org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile
... java.lang.reflect.InvocationTargetException: null
Caused by: java.lang.IllegalAccessError: tried to access class
org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile ...

All these errors were the result of the wrong use of methods on the receiving file.

For example, casting a MultipartFile to MultipartFile isn’t actually allowed:

StandardMultipartHttpServletRequest.StandardMultipartFile = params.file //wrong

Another example we encountered was IOFileUploadException:
org.springframework.web.multipart.MultipartException:
Could not parse multipart servlet request; nested exception is java.io.IOException:
org.apache.tomcat.util.http.fileupload.FileUploadBase$IOFileUploadException:
Processing of multipart/form-data request failed. null

The reason for this error was WiFi connection abort on the mobile phone sending the file.