Simple posting of multipart/form-data from Android

Detailing how to POST multipart form data from an Android client. Here I post a video file captured from the camera. The actual code here is very simple. The real challenge was getting access to the necessary libraries, and getting the project to build and run with no errors. I put this together from many different sources, too many to bother documenting, where each source (SO article, blog, etc) provided insight into some small part of the overall solution.

I am using Android Studio 1.0.2 with gradle (standard set up). Before we get to the actual code you'll need to manage the dependencies.

Dependencies

For more on handling dependecies in Android Studio read this.

To get your libraries compiled with no errors open your module's build.gradle file (not the project gradle file) and add this to the dependencies section:

compile('org.apache.httpcomponents:httpmime:4.+') {
    exclude module: "httpclient"
}
compile('org.apache.httpcomponents:httpcore:4.+') {
    exclude module: "httpclient"
}

With this gradle will compile in the dependecies you need without errors caused by collision in namespaces between the Apache HttpClient module provided by the Android SDK.

Gradle errors re: duplicate files

If you build your project now you'll get errors from gradle about duplicate files in META-INF. To fix this I added the following to my module's build.gradle immediatley following the dependencies section.

android {
  packagingOptions {
      exclude 'META-INF/DEPENDENCIES'
      exclude 'META-INF/NOTICE'
      exclude 'META-INF/LICENSE'
  }
}

This workaround excludes these files from the package therebye avoiding the file duplication. Apparently this is a gradle bug that will go away at some point in the future.

Manifest permissions

To use the camera and the network you need to ask nicely. Open your project's AndroidManifest.xml file and add:

<!-- Get permission to Write (and implicitly Read) to publicly accessible external storage -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- Get permission to record audio (do you want audio in your video?) -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- Allow access to internet -->
<uses-permission android:name="android.permission.INTERNET" />

Import your classes

You will need to add the following to the class that handles your POST:

import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.util.EntityUtils;

Upload a video file

1. Handle the activity result

To read about using the video capture intent check this tutorial out. The intent returns and calls my onActivityResult method and I deal with it like this:

@Override
 /*
 * Called by system when an Activity passes a return Intent back to this Activity.
 */
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {

    // Ensure we are handling the right request and the response was :+1:
    if (requestCode == REQUEST_VIDEO_CAPTURE && resultCode == RESULT_OK) {

        // Get (file) URI of the vid from the return Intent's data
        Uri videoUri = intent.getData();
        String selectedPath = getPath(videoUri);

        // Actually upload the video to the server
        uploadVideo(selectedPath);
    }
}

2. Getting the path to the video file

Get the path to the file given the URI.

private String getPath(Uri uri) {
    String[] projection = {MediaStore.Video.Media.DATA, MediaStore.Video.Media.SIZE, MediaStore.Video.Media.DURATION};
    Cursor cursor = getContentResolver().query(uri, projection, null, null, null);
    cursor.moveToFirst();
    return cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA));
}

3. Upload the file to the server

We upload the video file here using MultipartEntityBuilder instead of the now deprecated MultipartEntity.

private void uploadVideo(String videoPath) {
    try
    {
        HttpClient client = new DefaultHttpClient();
        File file = new File(videoPath);
        HttpPost post = new HttpPost(server +"/api/docfile/");

        MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create();
        entityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
        entityBuilder.addBinaryBody("uploadfile", file);
        // add more key/value pairs here as needed

        HttpEntity entity = entityBuilder.build();
        post.setEntity(entity);

        HttpResponse response = client.execute(post);
        HttpEntity httpEntity = response.getEntity();

        Log.v("result", EntityUtils.toString(httpEntity));
    }
    catch(Exception e)
    {
        e.printStackTrace();
    }
}

Notes

This is not intended to be production ready. It's just a proof of concept. At the very least you'd need to do your uploads in an AsyncTask to get them off the main thread. Additional concerns are large files, no network, interrupted uploads, etc.

Also, the code here was taken and adapted from numerous SO articles and other sources. I've just pieced this together from my own research.