Tracking Multiple Page File Uploads Using OKHTTP

I am trying to implement a progress bar to indicate the progress of a multi-file file download.

I read from a comment on this answer - https://stackoverflow.com/a/4646262/2126326 that I should wrap the receiver passed to RequestBody and provide a callback that tracks the bytes moved,

I created a custom RequestBody and wrapped the receiver with the CustomSink class, however, through debugging, I see that the bytes are being written to RealBufferedSink ln 44, and the user-defined receiver recording method is run only once, not allowing me to track the moved bytes.

private class CustomRequestBody extends RequestBody { MediaType contentType; byte[] content; private CustomRequestBody(final MediaType contentType, final byte[] content) { this.contentType = contentType; this.content = content; } @Override public MediaType contentType() { return contentType; } @Override public long contentLength() { return content.length; } @Override public void writeTo(BufferedSink sink) throws IOException { CustomSink customSink = new CustomSink(sink); customSink.write(content); } } private class CustomSink implements BufferedSink { private static final String TAG = "CUSTOM_SINK"; BufferedSink bufferedSink; private CustomSink(BufferedSink bufferedSink) { this.bufferedSink = bufferedSink; } @Override public void write(Buffer source, long byteCount) throws IOException { Log.d(TAG, "source size: " + source.size() + " bytecount" + byteCount); bufferedSink.write(source, byteCount); } @Override public void flush() throws IOException { bufferedSink.flush(); } @Override public Timeout timeout() { return bufferedSink.timeout(); } @Override public void close() throws IOException { bufferedSink.close(); } @Override public Buffer buffer() { return bufferedSink.buffer(); } @Override public BufferedSink write(ByteString byteString) throws IOException { return bufferedSink.write(byteString); } @Override public BufferedSink write(byte[] source) throws IOException { return bufferedSink.write(source); } @Override public BufferedSink write(byte[] source, int offset, int byteCount) throws IOException { return bufferedSink.write(source, offset, byteCount); } @Override public long writeAll(Source source) throws IOException { return bufferedSink.writeAll(source); } @Override public BufferedSink writeUtf8(String string) throws IOException { return bufferedSink.writeUtf8(string); } @Override public BufferedSink writeString(String string, Charset charset) throws IOException { return bufferedSink.writeString(string, charset); } @Override public BufferedSink writeByte(int b) throws IOException { return bufferedSink.writeByte(b); } @Override public BufferedSink writeShort(int s) throws IOException { return bufferedSink.writeShort(s); } @Override public BufferedSink writeShortLe(int s) throws IOException { return bufferedSink.writeShortLe(s); } @Override public BufferedSink writeInt(int i) throws IOException { return bufferedSink.writeInt(i); } @Override public BufferedSink writeIntLe(int i) throws IOException { return bufferedSink.writeIntLe(i); } @Override public BufferedSink writeLong(long v) throws IOException { return bufferedSink.writeLong(v); } @Override public BufferedSink writeLongLe(long v) throws IOException { return bufferedSink.writeLongLe(v); } @Override public BufferedSink emitCompleteSegments() throws IOException { return bufferedSink.emitCompleteSegments(); } @Override public OutputStream outputStream() { return bufferedSink.outputStream(); } } 

Does anyone have an example of how I will do this?

+31
android okio
Sep 21 '14 at 18:42
source share
3 answers

You need to create a custom RequestBody and override the writeTo method, and there you have to send your files to the sink in segments. It is very important that you drop the sink after each segment, otherwise the progress bar will fill up quickly if the file is not sent over the network because the contents will remain in the receiver (which acts as a buffer).

 public class CountingFileRequestBody extends RequestBody { private static final int SEGMENT_SIZE = 2048; // okio.Segment.SIZE private final File file; private final ProgressListener listener; private final String contentType; public CountingFileRequestBody(File file, String contentType, ProgressListener listener) { this.file = file; this.contentType = contentType; this.listener = listener; } @Override public long contentLength() { return file.length(); } @Override public MediaType contentType() { return MediaType.parse(contentType); } @Override public void writeTo(BufferedSink sink) throws IOException { Source source = null; try { source = Okio.source(file); long total = 0; long read; while ((read = source.read(sink.buffer(), SEGMENT_SIZE)) != -1) { total += read; sink.flush(); this.listener.transferred(total); } } finally { Util.closeQuietly(source); } } public interface ProgressListener { void transferred(long num); } } 

You can find the full implementation that supports displaying progress in AdapterView, as well as canceling the download in my essence: https://gist.github.com/eduardb/dd2dc530afd37108e1ac

+53
Oct. 15 '14 at 7:22
source
  • We just need to create a custom RequestBody , no need to implement custom BufferedSink . We can allocate the Okio buffer for reading from the image file and connect this buffer to the stream.

See the createCustomRequestBody function below for an example.

 public static RequestBody createCustomRequestBody(final MediaType contentType, final File file) { return new RequestBody() { @Override public MediaType contentType() { return contentType; } @Override public long contentLength() { return file.length(); } @Override public void writeTo(BufferedSink sink) throws IOException { Source source = null; try { source = Okio.source(file); //sink.writeAll(source); Buffer buf = new Buffer(); Long remaining = contentLength(); for (long readCount; (readCount = source.read(buf, 2048)) != -1; ) { sink.write(buf, readCount); Log.d(TAG, "source size: " + contentLength() + " remaining bytes: " + (remaining -= readCount)); } } catch (Exception e) { e.printStackTrace(); } } }; } 
  • -

     .addPart( Headers.of("Content-Disposition", "form-data; name=\"image\""), createCustomRequestBody(MediaType.parse("image/png"), new File("test.jpg"))) .build() 
+10
Oct 13 '14 at 10:11
source

This thing works great!

Gradle

 dependencies { compile 'io.github.lizhangqu:coreprogress:1.0.2' } //wrap your original request body with progress RequestBody requestBody = ProgressHelper.withProgress(body, new ProgressUIListener()....} 

The full example code is here https://github.com/lizhangqu/CoreProgress

+1
Nov 06 '17 at 9:25
source



All Articles