el producer
el producer

Reputation: 321

Get Progress of File Upload using HttpPost in Android

I am trying to get the progress of the actual file upload using HttpPost. I have a stable solution so far (that i found here in SO) that has a progress but after uploading large files i realized it only counts the bytes written to the output buffer NOT the post transfer progress. I would like to somehow get the progress of the actual "post". Can someone explain how I can achieve this using what I worked so hard to get so far? Most of the solutions I have found online are only counting bytes written to output buffer (which is good enough for small files but not when transferring large files).

public static String postFile(final Context context, String fileName) throws Exception {

    HttpClient client = new DefaultHttpClient();
    HttpPost post = new HttpPost("http://my.url/");
    MultipartEntityBuilder builder = MultipartEntityBuilder.create();        
    builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);

    final File file = new File(fileName);
    final long totalSize = file.length();
    FileBody fb = new FileBody(file);

    builder.addPart("uploaded_file", new FileBody(new File(fileName)));


    final HttpEntity yourEntity = builder.build();

    int progressPercent = 0;

    class ProgressiveEntity implements HttpEntity {
        @Override
        public void consumeContent() throws IOException {
            yourEntity.consumeContent();                
        }
        @Override
        public InputStream getContent() throws IOException,
                IllegalStateException {
            return yourEntity.getContent();
        }
        @Override
        public Header getContentEncoding() {             
            return yourEntity.getContentEncoding();
        }
        @Override
        public long getContentLength() {
            return yourEntity.getContentLength();
        }
        @Override
        public Header getContentType() {
            return yourEntity.getContentType();
        }
        @Override
        public boolean isChunked() {             
            return yourEntity.isChunked();
        }
        @Override
        public boolean isRepeatable() {
            return yourEntity.isRepeatable();
        }
        @Override
        public boolean isStreaming() {             
            return yourEntity.isStreaming();
        } // CONSIDER put a _real_ delegator into here!

        @Override
        public void writeTo(OutputStream outstream) throws IOException {

            class ProxyOutputStream extends FilterOutputStream {

                public ProxyOutputStream(OutputStream proxy) {
                    super(proxy);    
                }
                public void write(int idx) throws IOException {
                    out.write(idx);
                }
                public void write(byte[] bts) throws IOException {
                    out.write(bts);
                }
                public void write(byte[] bts, int st, int end) throws IOException {
                    out.write(bts, st, end);
                }
                public void flush() throws IOException {
                    out.flush();
                }
                public void close() throws IOException {
                    out.close();
                }
            } // CONSIDER import this class (and risk more Jar File Hell)

            class ProgressiveOutputStream extends ProxyOutputStream {
                long totalSent;
                public ProgressiveOutputStream(OutputStream proxy) {
                       super(proxy);
                       totalSent = 0;
                }

                public void write(byte[] bts, int st, int end) throws IOException {

                // end is the amount being sent this time
                // st is always zero and end=bts.length()

                     totalSent += end;
                     int progress = (int) ((totalSent / (float) totalSize) * 100);
                     out.write(bts, st, end);
                }
            }

            yourEntity.writeTo(new ProgressiveOutputStream(outstream));
        }

    };



    ProgressiveEntity myEntity = new ProgressiveEntity();

    post.setEntity(myEntity);

    //Output to buffer is complete at this point!
    HttpResponse response = client.execute(post);        

    String jsonResponseStr = getContent(response);

    Log.d("MYTAG",jsonResponseStr);

    return jsonResponseStr;

} 

In my receiving script on the remote server, I am only echo-ing a string so that I can send an immediate response (no file/database processing at all) and the response from server still takes a very long time. I strongly believe at this point the transfer happens after write to buffer completes.

Upvotes: 1

Views: 2785

Answers (5)

Adnan Abdollah Zaki
Adnan Abdollah Zaki

Reputation: 4406

class ProgressiveOutputStream extends ProxyOutputStream {
            long totalSent;
            public ProgressiveOutputStream(OutputStream proxy) {
                   super(proxy);
                   totalSent = 0;
            }

            public void write(byte[] bts, int st, int end) throws IOException {

            // FIXME  Put your progress bar stuff here!
            // end is the amount being sent this time
            // st is always zero and end=bts.length()

                 totalSent += end;
                 progress.publish((int) ((totalSent / (float) totalSize) * 100));
                 out.write(bts, st, end);
            }

Upvotes: 1

novachevskyi
novachevskyi

Reputation: 183

Please try next solution instead of using ProxyOutputStream in your writeTo method:

@Override 
public void writeTo(OutputStream outstream) throws IOException {
    ByteArrayInputStream reader = new ByteArrayInputStream(mImageData);
    byte[] fileBuffer = new byte[2048];
    int bytesRead;
    while ((bytesRead = reader.read(fileBuffer)) != -1) {
        outstream.write(fileBuffer, 0, bytesRead);
        int progress = bytesRead;
    }
    reader.close();
    yourEntity.writeTo(outstream);
}

Upvotes: 0

Maro
Maro

Reputation: 253

maybe you could flush the data every time you call write:

totalSent += end;
int progress = (int) ((totalSent / (float) totalSize) * 100);
out.write(bts, st, end);
out.flush(); //flush

Edit: you can also try this since i get the feeling end represents the end index in the outputstream named out:

totalSent += (end-st);

Upvotes: 0

Luchi Valles
Luchi Valles

Reputation: 181

I know this is quite old, but I've just found the solution. If totalSize is your entity content length, then you can base your progress on that:

class ProgressiveOutputStream extends ProxyOutputStream {
                    long totalSent;
                    long totalSize;
                    public ProgressiveOutputStream(OutputStream proxy, long total) {
                        super(proxy);
                        totalSent = 0;
                        totalSize = total;
                    }
                    public void write(byte[] bts, int st, int end) throws IOException {
                        totalSent += end;
                        publishProgress((int) ((totalSent / (float) totalSize) * 100));
                        out.write(bts, st, end);
                    }
                }

                yourEntity.writeTo(new ProgressiveOutputStream(outstream, yourEntity.getContentLength()));

You update your progressbar in OnProgressUpdate of your asyncTask (pb is the progressBar):

@Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
        pb.setProgress(values[0]);
    }

Upvotes: 0

el producer
el producer

Reputation: 321

Since I don't see any solution for this, I suppose the answer is to use a spinning animation without a progress percent. Since nothing can be done until the transmission is complete anyway. Oh well... atleast it solved my problem.

Upvotes: 0

Related Questions