Can't grab progress on http POST file upload (Android) Can't grab progress on http POST file upload (Android) android android

Can't grab progress on http POST file upload (Android)


I have tried quite a lot in the last days and I think I have the answer to the initial question:

It's not possible to grab a progress using HttpURLConnection because there is a bug / unusual behavior in Android:http://code.google.com/p/android/issues/detail?id=3164#c6

It will be fixed post Froyo, which is not something one can wait for...

So, the only other option I found is to use the Apache HttpClient. The answer linked by papleu is correct, but it refers to general Java. The classes that are used there are not available in Android anymore (HttpClient 3.1 was part of the SDK, once). So, what you can do is add the libraries from HttpClient 4 (specifically apache-mime4j-0.6.jar and httpmime-4.0.1.jar) and use a combination of the first answer (by Tuler) and the last answer (by Hamy):

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 CountingMultipartEntity extends MultipartEntity {    private final ProgressListener listener;    public CountingMultipartEntity(final ProgressListener listener) {        super();        this.listener = listener;    }    public CountingMultipartEntity(final HttpMultipartMode mode, final ProgressListener listener) {        super(mode);        this.listener = listener;    }    public CountingMultipartEntity(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);        }    }}

This works with HttpClient 4, but the downside is that my apk now has a size of 235 kb (it was 90 kb when I used the multipart upload described in my question) and, even worse, an installed app size of 735 kb (about 170 kb before).That's really awful. Only to get a progress during upload the app size is now more than 4 times as big as it was before.


Try this out. It works.

connection = (HttpURLConnection) url_stripped.openConnection();    connection.setRequestMethod("PUT");    String boundary = "---------------------------boundary";    String tail = "\r\n--" + boundary + "--\r\n";    connection.addRequestProperty("Content-Type", "image/jpeg");    connection.setRequestProperty("Connection", "Keep-Alive");    connection.setRequestProperty("Content-Length", ""            + file.length());    connection.setDoOutput(true);    String metadataPart = "--"            + boundary            + "\r\n"            + "Content-Disposition: form-data; name=\"metadata\"\r\n\r\n"            + "" + "\r\n";    String fileHeader1 = "--"            + boundary            + "\r\n"            + "Content-Disposition: form-data; name=\"uploadfile\"; filename=\""            + fileName + "\"\r\n"            + "Content-Type: application/octet-stream\r\n"            + "Content-Transfer-Encoding: binary\r\n";    long fileLength = file.length() + tail.length();    String fileHeader2 = "Content-length: " + fileLength + "\r\n";    String fileHeader = fileHeader1 + fileHeader2 + "\r\n";    String stringData = metadataPart + fileHeader;    long requestLength = stringData.length() + fileLength;    connection.setRequestProperty("Content-length", ""            + requestLength);    connection.setFixedLengthStreamingMode((int) requestLength);    connection.connect();    DataOutputStream out = new DataOutputStream(            connection.getOutputStream());    out.writeBytes(stringData);    out.flush();    int progress = 0;    int bytesRead = 0;    byte buf[] = new byte[1024];    BufferedInputStream bufInput = new BufferedInputStream(            new FileInputStream(file));    while ((bytesRead = bufInput.read(buf)) != -1) {        // write output        out.write(buf, 0, bytesRead);        out.flush();        progress += bytesRead;        // update progress bar        publishProgress(progress);    }    // Write closing boundary and close stream    out.writeBytes(tail);    out.flush();    out.close();    // Get server response    BufferedReader reader = new BufferedReader(            new InputStreamReader(connection.getInputStream()));    String line = "";    StringBuilder builder = new StringBuilder();    while ((line = reader.readLine()) != null) {        builder.append(line);    }

Reference: http://delimitry.blogspot.in/2011/08/android-upload-progress.html


It is possible to grab the progress from the DefaultHttpClient. The fact that it stays in the line

response = defaultHttpClient.execute( httpput, context );

until complete data is sent, does not necessarily mean that you cannot grab the progress. Namely, HttpContext changes while executing this line, so if you approach it via different Thread, you may observe changes in that. What you need is ConnAdapter. It has HttpConnectionMetrics embedded

final AbstractClientConnAdapter connAdapter = (AbstractClientConnAdapter) context.getAttribute(ExecutionContext.HTTP_CONNECTION);final HttpConnectionMetrics metrics = connAdapter.getMetrics();int bytesSent = (int)metrics.getSentBytesCount();

If you check this periodically, you will observe the progress. Be sure to correctly handle threads, and all try/catch guarded. Especially, handle the case when Context is not valid any more (IllegalStateException), which occurs when Put is canceled or completed.