Jun 252011
 

Shazam Being able to display a progress bar during a time consuming upload to a web server is important when dealing with users and appeasing their impatience. Here is one approach of achieving this.

In this example we are going to use 2 classes – the first one is going to implement Android’s really handy threading function: Async Task and the other is going to extend MutlipartEntity – the basic object used for a multipart POST. Let’s take a look at extending a MultipartEntity object:

CustomMultiPartEntity.java

import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
 
public class CustomMultiPartEntity extends MultipartEntity
{
 
	private final ProgressListener listener;
 
	public CustomMultiPartEntity(final ProgressListener listener)
	{
		super();
		this.listener = listener;
	}
 
	public CustomMultiPartEntity(final HttpMultipartMode mode, final ProgressListener listener)
	{
		super(mode);
		this.listener = listener;
	}
 
	public CustomMultiPartEntity(HttpMultipartMode mode, final String boundary, final Charset charset, final ProgressListener listener)
	{
		super(mode, boundary, charset);
		this.listener = listener;
	}
 
	@Override
	public void writeTo(final OutputStream outstream) throws IOException
	{
		super.writeTo(new CountingOutputStream(outstream, this.listener));
	}
 
	public static interface ProgressListener
	{
		void transferred(long num);
	}
 
	public static class CountingOutputStream extends FilterOutputStream
	{
 
		private final ProgressListener listener;
		private long transferred;
 
		public CountingOutputStream(final OutputStream out, final ProgressListener listener)
		{
			super(out);
			this.listener = listener;
			this.transferred = 0;
		}
 
		public void write(byte[] b, int off, int len) throws IOException
		{
			out.write(b, off, len);
			this.transferred += len;
			this.listener.transferred(this.transferred);
		}
 
		public void write(int b) throws IOException
		{
			out.write(b);
			this.transferred++;
			this.listener.transferred(this.transferred);
		}
	}
}

By simply counting the amount of bytes that are written, we can implement an interface (here we called it trasnfered())which can be called in our main class to update our progress bar dialog box:

Main.java

private class HttpMultipartPost extends AsyncTask<HttpResponse, Integer, TypeUploadImage>
	{
		ProgressDialog pd;
		long totalSize;
 
		@Override
		protected void onPreExecute()
		{
			pd = new ProgressDialog(this);
			pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
			pd.setMessage("Uploading Picture...");
			pd.setCancelable(false);
			pd.show();
		}
 
		@Override
		protected TypeUploadImage doInBackground(HttpResponse... arg0)
		{
			HttpClient httpClient = new DefaultHttpClient();
			HttpContext httpContext = new BasicHttpContext();
			HttpPost httpPost = new HttpPost("http://herpderp.com/UploadImage.php");
 
			try
			{
				CustomMultipartEntity multipartContent = new CustomMultipartEntity(new ProgressListener()
				{
					@Override
					public void transferred(long num)
					{
						publishProgress((int) ((num / (float) totalSize) * 100));
					}
				});
 
				// We use FileBody to transfer an image
				multipartContent.addPart("uploaded_file", new FileBody(new File(m_userSelectedImagePath)));
				totalSize = multipartContent.getContentLength();
 
				// Send it
				httpPost.setEntity(multipartContent);
				HttpResponse response = httpClient.execute(httpPost, httpContext);
				String serverResponse = EntityUtils.toString(response.getEntity());
 
				ResponseFactory rp = new ResponseFactory(serverResponse);
				return (TypeImage) rp.getData();
			}
 
			catch (Exception e)
			{
				System.out.println(e);
			}
			return null;
		}
 
		@Override
		protected void onProgressUpdate(Integer... progress)
		{
			pd.setProgress((int) (progress[0]));
		}
 
		@Override
		protected void onPostExecute(TypeUploadImage ui)
		{
			pd.dismiss();
		}
	}

By calling the transferred() function we implemented in CustomMultipartPost in “onProgressUpdate()”, we can use the built-in function setProgress() of a ProgressDialog to notify the user. Happy POSTing!

  • Johnny

    Shouldn’t AsyncTask have types? Android’s docs (http://developer.android.com/reference/android/os/AsyncTask.html) have an example: private class DownloadFilesTask extends AsyncTask.

    • http://toolongdidntread.com John Russell

      You are completely correct – for whatever reason I didn’t copy it over correctly. Changes have been made – thanks!

  • granan

    Could You include here implementation of this class:

    ResponseFactory
    TypeUploadImage
    return (TypeImage) rp.getData();

    • Wongstar Iac

      hi granan:

      thank for you answer my question.

      As far as I know ,use execute will post the data to server

      HttpResponse response = httpClient.execute(httpPost, httpContext);

      but i don’t know why use below comman,

      String serverResponse = EntityUtils.toString(response.getEntity());

      ResponseFactory rp = new ResponseFactory(serverResponse);
      return (TypeImage) rp.getData();

      Can you help me to explain it? thanks!

  • ovidiu roatis

    Thanks for the solution, almost works fine for me, only that there is a huge jump between 1% and 99% with nothing in between. Any ideas why? I am testing on a Samsung Galaxy, with Android 2.3.4.

  • Tomtom

    Hi!
    Thanks for this snippet, it will be very usefull for me! But as “ovidiu roatis” said, the code doesn’t show the progression of the upload. It jumps from 0 to 100 directly, even if i set in the emulator GPRS as data connection.

    Do you have any idea why i get this behavior?

    • http://toolongdidntread.com John Russell

      Have you tried double checking your onProgressUpdate() function? Make sure you are using a ProgressDialog dialog. I’ve used this identical code for several projects and it definitely should show the progression. Try stepping through your code and seeing if multipartContent.getContentLength() returns the actual size of your entity.

  • 利华 贺

    It’s so useful, thanks a lot

  • Edasx

    Hello! I mplemented this and have the similar 0 – 99/100 jump. The reason for this is I am uploading only one large file (300KB) and few bytes of description… If i put 3 files in multipart of the same size it skips over from 33-66-100… What we have to do here is get more indepth to the byte level of the stream.

    • Edasx

      This should solve it

      public void write(byte[] b, int off, int len) throws IOException
      {
      int BUFFER_SIZE = 10000;
      int chunkSize = BUFFER_SIZE;
      int currentOffset = 0;
      int length = b.length;

      while (length>currentOffset) {
      chunkSize = length - currentOffset;
      if (chunkSize > BUFFER_SIZE) {
      chunkSize = BUFFER_SIZE;
      }
      out.write(b, currentOffset, chunkSize);
      currentOffset += chunkSize;
      this.transferred += chunkSize;
      this.listener.transferred(this.transferred);
      }

      }

      • Edasx

        Damn. I had an error there. For some reason you have to stick with passed len variable and don’t do b.length. =)


        public void write(byte[] b, int off, int len) throws IOException
        {
        int BUFFER_SIZE = 10000;
        int chunkSize;
        int currentOffset = 0;

        while (len>currentOffset) {
        chunkSize = len - currentOffset;
        if (chunkSize > BUFFER_SIZE) {
        chunkSize = BUFFER_SIZE;
        }
        out.write(b, currentOffset, chunkSize);
        currentOffset += chunkSize;
        this.transferred += chunkSize;
        //Log.i("CustomOutputStream WRITE","" + off + "|" + len + "|" + len + "|" + currentOffset + "|" + chunkSize + "|" + this.transferred);
        this.listener.transferred(this.transferred);
        }
        }

        • desgraci

          nice, OP should update your answer with this!!!

  • Ritchi3_kotzen

    Hi thanks for this.

    Just want to ask, when I use this instead of MultiPartEntity it doesn’t save to server and not posted to site.

  • Wongstar Iac

    hi ,i use this method to upload file to server and it can run,but when i open the server file ,it add more information in this file, how can i get the same file on the client and server?

  • Xesun4122

    how to add multipart example:Content-Disposition: form-data; name=”key”; filename=”unknown”rn”,

  • Houssem Zaier

    thanks a lot my friend u did a great job

  • http://www.facebook.com/alexey.schekin Alexey Schekin

    Thanks alot!!!

  • Zheng_jinmu

    OK, TKS a lot ,it can send up big file,

  • alihaluk

    Very good implementation except one thing; you trying to call publishProgress out of the AsyncTask class. Because of that, progressBar isnt updating on process. we just see %0 and %100.

    You should handle like call a public void instead of publishProgress in transferred (override class). Then call publishProgress in the void. This should be works fine.

    • vivian lee

      hi, i got the same problem just show %0 ,%100, i want know how do you solve this?
      thanks a lot

  • Rashed Addosary

    You’re a genius. You saved my day
    Thank you John

  • Krishna Lal Shrestha

    with this class CustomMultiPartEntity, i am unable to add StringBody. case arises when passing image with userid.

  • Steffen

    Workes like a charm!

    • mirko77

      Have you tried with more than 1 image per request??

  • mirko77

    It does not work for multiple images, I guess it takes the amount of bytes from the first image only

    • utpal

      Have you got for multiple image?

      Thanks

  • Carlos Navas

    I have a “Cancel button” which will cancel the current upload. How can I cancel completely this task?? Does anybody have idea?
    multipartContent.consumeContent(); httpPost.abort(); didn’t work

    Thanks in advance

  • Jganzabal

    Simply Perfect!

  • nihar bhatt

    i have used your code but it stucks at 100% and completes when server returns the respons so eventually it moves pretty fast from 0 to 99 and stucks at 100 u have any idea

    • muffie

      Maybe you are using .get() on your AsyncTask which blocks the UI thread. Then the progress is just updated after the task is completed. Guess thats why it moves so fast from 0 – 99. Solution could be to append the result value onPostExecute to a variable of the calling UI element instead of calling .get()..
      Hope that helps

  • http://www.facebook.com/ramesh.kec.85 Ramesh Akula

    I m grateful with your solution. But there is a problem that it struck after 100% for a while. Can u help me to explain this. ?

  • Maike

    Thanks!

  • jacki

    guys what is TypeUploadImage????

  • Carlos Navas

    This tutorial is awesome. It helped me a lot but I have a problem, if the file is too small the progress jump from 0 to 100%. I tried the solution given by Edasx without luck. Anybody know what could be the trick? Thanks!

  • http://xavi.co Xavi

    Why are many of the parameters to the CustomMultiPartEntity and CountingOutputStream constructors marked as final?

  • Jonas

    I have an issue. I try to upload an image on 3g. Works fine and progress counts slowly towards 100.
    But when i reach 100 it takes another 30 sec before the upload is finished.

    I echo progress like this in transferred():
    Log.d(TAG,”transfered: “+num+”/”+totalSize);
    and then in onprogressupdate:
    Log.d(TAG,”progress: “+progress[0]);

    06-23 13:26:05.996 29884-30214/ D/ShowTransaction: transfered: 3004910/3004912
    06-23 13:26:05.996 29884-30214/ D/ShowTransaction: transfered: 3004912/3004912
    06-23 13:26:06.006 29884-29884/ D/ShowTransaction: progress: 99
    06-23 13:26:06.006 29884-29884/ D/ShowTransaction: progress: 99
    06-23 13:26:06.006 29884-29884/ D/ShowTransaction: progress: 99
    06-23 13:26:06.006 29884-29884/ D/ShowTransaction: progress: 99
    06-23 13:26:06.006 29884-29884/ D/ShowTransaction: progress: 100
    06-23 13:26:24.245 29884-30214/ D/ShowTransaction: DONE,response: [{"key":"image","name":"320.jpg","type":"image/jpeg","size":3004705,"hash":"1ea3d418fd3ca34d7ed14eab97e839d6","id":38}]

  • Afaci

    It works well with the packages
    *httpcore-4.3.jar
    *httpclient-4.3.jar
    *httpmime-4.3.jar
    *apache-mime4j-0.3.jar

  • Nasser

    yes Sometime when uploading large images, the progress jump from 0 to 100%

    this is the solution,

    in CustomMultiPartEntity.java, replace the second last method, (the method before the last method) with following code:

    1- the issue with old method, it tries to upload (write) each image with one call of out.write(byte[] b, int off, int len) ,

    most of the time, the byte [] b, will be very large,

    so the line:
    this.listener.transferred(this.transferred);
    will not be executed until out.write(); completed.

    what I did it to divide the original byte array into smaller bytes

    This is the new method:
    ————–

    public void write(byte[] b, int off, int len) throws IOException
    {

    int length = len;

    int offset = off;

    int buffer = 20480;//buffer size, I made it 20KB, 1024*20

    int remainer = len;

    while(length > offset){

    if(remainer < buffer)

    buffer = remainer;

    out.write(b, offset, buffer);

    remainer -= buffer;

    offset += buffer;

    this.transferred += buffer;

    this.listener.transferred(this.transferred);

    System.out.println("k:"+this.transferred);

    //out.flush();

    }
    }

    Best Regards,
    Nasser Al-Hadhrami

    • Pixwert

      Thankss!! this is what i was looking for…!

  • Nitin Zagade

    can u pls provide implementation
    ResponseFactory
    TypeUploadImage