I am trying to implement a a progress bar to indicate the progress of a multipart file upload.
I have read from a comment on this answer - https://stackoverflow.com/
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()....}
Full example code here https://github.com/lizhangqu/CoreProgress
You have to create a custom RequestBody and override writeTo method, and there you have to send your files down the sink in segments. It is very important that you flush the sink after each segment, otherwise your progress bar will fill up quickly without the file being actually sent over the network, because the contents will stay in the sink (which acts like 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 a complete implementation that supports displaying progress in an AdapterView and also cancelling uploads at my gist: https://gist.github.com/eduardb/dd2dc530afd37108e1ac
RequestBody
, no need to implement custom BufferedSink
. We can allocate Okio buffer to read from image file, and connect this buffer to sink.For an example, please see the below createCustomRequestBody
function
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();
}
}
};
}
to use -
.addPart(
Headers.of("Content-Disposition", "form-data; name=\"image\""),
createCustomRequestBody(MediaType.parse("image/png"), new File("test.jpg")))
.build()