Android development blog
Tutorials about Android dev topics

Android Apache HTTP Client: Post, Download, Upload multipart

Topics covered

Apache HTTP client

Post request

Download binary data

Upload data

Exchange cookies

In this post I want to describe how to build an Apache HTTP client . In one of my post I describe how we can use HttpUrlConnection to create a client. Using Apache  HTTP library I want to explain how we can send POST request, download images and upload multipart binary data. Moreover I will describe how to exchange cookies.

POST Request

As we know already POST and GET are the basic method that we can use to exchange data with a remote server.As example I will send to a remote server some data in text format (by now). So the first step is creating a DefaultHttpClient that is used a wrapper to send data.
HttpClient client = new DefaultHttpClient();

Once we have our client we have to instantiate a class that handles POST request:
HttpPost post = new HttpPost(url);
where url is the url we want to invoke. To send data we simply have to create a list of NameValuePair a simple class that holds the name and the value of our parameters we want to send.
List<NameValuePair> nvList = new ArrayList<NameValuePair>();
BasicNameValuePair bnvp = new BasicNameValuePair("name", name);
// We can add more
nvList.add(bnvp);
In this case at line 2 we set the name of the parameter as name and we add the bnvp to the list as said before. Now we have build our list and set it inside our post request so that it can be sent:
post.setEntity(new UrlEncodedFormEntity(nvList));

In this case we encode our parameters using Url encoding. The last step is executing the request through our DefaultHttpClient instance. As a result we obtain an HttpResponse that can be used to read the result of our request:
HttpResponse resp = client.execute(post);

To read the response we can obtain an InputStream (line 1) and consume it reading data in this way:
InputStream is  = resp.getEntity().getContent();            
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder str = new StringBuilder();
String line = null;
while((line = reader.readLine()) != null){
    str.append(line + "\n");
}
is.close();
buffer.append(str.toString());    

At the end we have a string that we can use to update our UI.
We have to remember that this operation can be time consuming so that we have to execute this request in an AsyncTask. For more information read the post ‘Android HTTP Client: GET, POST, Download, Upload, Multipart Request’.It is enough to say that all we have done by now should be inside the doInBackground method. Running the example we have:
android_http_apache_client


Server side
android_http_apache_client_server


Download binary data

Another common operation is downloading binary data from sever. In this case we want to download an image from the server and show it in our UI.

The operation is very simple because in this case we have to make an HTTP Post request, passing the image name (line 7-10) and read the binary response (line 15-28):
@Override
protected byte[] doInBackground(String... params) {
    String url = params[0];
    String name = params[1];
    
    
    HttpClient client = new DefaultHttpClient();
    HttpPost post = new HttpPost(url);
    List<NameValuePair> paramList = new ArrayList<NameValuePair>();
    paramList.add(new BasicNameValuePair("name", name));
    byte[] data = null;
    
    try {
        post.setEntity(new UrlEncodedFormEntity(paramList));
        HttpResponse resp = client.execute(post);
        InputStream is = resp.getEntity().getContent();
        int contentSize = (int) resp.getEntity().getContentLength();
        System.out.println("Content size ["+contentSize+"]");
        BufferedInputStream bis = new BufferedInputStream(is, 512);
        
        data = new byte[contentSize];
        int bytesRead = 0;
        int offset = 0;
        
        while (bytesRead != -1 && offset < contentSize) {
            bytesRead = bis.read(data, offset, contentSize - offset);
            offset += bytesRead;
        } 
    }
    catch(Throwable t) {
        // Handle error here
        t.printStackTrace();
    }
    
    
    return data;
}

At line 17 we read the response length so that we can create a byte array with the same size (see line 21), then at line 19 we create a buffered input stream to read the response stream. Then we simply read the response filling the data byte array. As result we have:

android_http_apache_binary_data


Upload binary data


This is one of most interesting aspect. To handle this operation we need to add an external library to simplify the task. This is a drawback of using apache http client respect to the android native api. On the other side as we saw android native api doesn’t handle multipart request so we have to do everything from zero. Using this library we can simplify the process but we have as a consequence a bigger apk at the end. The library is open source and it is called httpmime-xxx.jar. You can download it here. Remember to add it to your project and has to be exported (see Order and Export in Eclipse). As example we create a multipart request containing some text data and an image. So we start creating always our DefaultHttpClient and HttpPost:
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(url);

Now we have to create our post content, called Entity:
MultipartEntity multiPart = new MultipartEntity();

and then add the single parts: two are text data and one is the binary data (the image we want to upload):
multiPart.addPart("param1", new StringBody(param1));
multiPart.addPart("param2", new StringBody(param2));
multiPart.addPart("file", new ByteArrayBody(baos.toByteArray(), "logo.png"));

Notice at line 3 we create a part using a ByteArrayBody to contain the binary data. The we fill the post content with the data we created:
post.setEntity(multiPart);

and then we simply send (or post) it to the server:
client.execute(post);

Running the app we have:


android_http_apache_client_upload


Server side

android_http_apache_client_upload_server


Exchange Cookie

One interesting aspect in HTTP protocol is the cookie management. As we know HTTP is a stateless protocol so using cookies we can persist some information across HTTP requests. As example we can suppose to make two HTTP request: one where we invoke a URL and the server returns a cookie containing some information and another one where we send back to the server the cookie.

The first request is very simple:
HttpPost post = new HttpPost(url);
HttpResponse resp = client.execute(post);

Then we read the cookie (line 1,3):
CookieStore store = ((DefaultHttpClient) client).getCookieStore();

List<Cookie> cookies = store.getCookies();
if (cookies != null) {
    for (Cookie c : cookies) { 
        System.out.println("Name ["+c.getName()+"] - Val ["+c.getValue()+"] 
                      - Domain ["+c.getDomain()+"] - Path ["+c.getPath()+"]");
        store.addCookie(c);
    }
}

At line 1 we simply get the cookie store where the cookies are stored. At line 3 we retrieves the cookies list. In the second post request we have to maintain the cookie we retrieved in the first request so we have:
HttpContext ctx = new BasicHttpContext();
ctx.setAttribute(ClientContext.COOKIE_STORE, store);

// Post again with cookie 
HttpPost post1 = new HttpPost(url);
client.execute(post1);
HttpResponse resp1 = client.execute(post, ctx);

At line 1 we create BasicHttpContext to handle cookies and at line 2 we set the store inside our context and finally at line 7 we execute the request passing the context too.

One thing we have to notice is that the DefaultHttpClient is always the same so that we can re-use the cookies.

Running the example we have:

android_http_apache_client_cookie

android_http_apache_client_server_cookie
Client side cookie outputServer side cookie output

Source code available at github.

8 comments:

  1. Google now discourages the use of DefaultHttpClient() and instead recommends HttpURLConnection.

    DefaultHttpClient
    DefaultHttpClient
    DefaultHttpClient

    ReplyDelete
  2. survivingwithandroidFebruary 19, 2014 at 5:18 PM

    Yes but it is always an option

    ReplyDelete
  3. I looked into HttpURLConnection to rewrite my old DefaultHttpClient but damn HttpURLConnection is an ugly API!

    ReplyDelete
  4. survivingwithandroidFebruary 20, 2014 at 7:46 PM

    You can use Volley. It is very interesting lib, you can give a look here:http://www.survivingwithandroid.com/2013/11/android-volley-tutorial-post-download.html

    ReplyDelete
  5. Hi host... I am a very new Android Apps coder and also new at WAMP server.... From your java codes, I have edited the lines as below:

    From:
    private String url = "http://10.0.2.2:8080/TestAndroid/TestServlet";

    To:
    private String url = "http://http://192.168.43.65:8080/jack";

    "jack" is a directory I created under C:\wamp\www



    Then I try to launch the code from SDK emulator, I try to type some words and send, but I can't read back my words from RESPONSE box...


    Do i need any PHP script under my "jack" directory?

    ReplyDelete
    Replies
    1. You should have a script or a servlet to have the response

      Delete

Related Posts with Thumbnails