使用OKHTTP跟踪多部分文件上传的进度
问题描述:
我想实现一个进度条来指示多部分文件上传的进度。使用OKHTTP跟踪多部分文件上传的进度
我已阅读对此答案的评论 - https://stackoverflow.com/a/24285633/1022454,我必须将传递给RequestBody的接收器包装起来,并提供跟踪移动字节的回调。
我已经创建了一个自定义的RequestBody,并用一个CustomSink类包装了接收器,但是通过调试,我可以看到该字节正在写入RealBufferedSink44并且自定义接收器写入方法只运行一次,不允许我跟踪移动的字节。
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();
}
}
有没有人有我如何去做这个事情的例子?
答
你必须创建一个自定义RequestBody并重写的writeTo方法,在那里你必须把你的文件发送到接收器中秒。在每个段之后清洗接收器是非常重要的,否则你的进度条会快速填满,而不会通过网络实际发送文件,因为内容将保留在接收器中(像缓冲器一样)。
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);
}
}
你可以找到一个支持一个AdapterView显示进度,并在我的主旨取消上传的完整实现:https://gist.github.com/eduardb/dd2dc530afd37108e1ac
答
- 我们只需要创建一个自定义
RequestBody
,不需要实现自定义BufferedSink
。我们可以分配Okio缓冲区来读取图像文件,并将此缓冲区连接到接收器。
有关示例,请参见下面的createCustomRequestBody
功能
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()
摇篮
完整的示例代码,这似乎并不因上载小文件通过速度较慢的网络连接工作。请参阅https://github.com/square/okhttp/issues/1078。这种情况是否有解决方案? – coalmee 2015-11-13 23:03:11
@Edy Bolos有没有办法将它与RxJava&Observable结合使用? – Sree 2016-03-16 19:53:46
回答你的问题:一切都可以包裹在一个可观察的:)但我必须离开别人去做。我唯一的建议是可能使用'BehaviorSubject'在'UploadsHandler'中发送进度值。 – 2016-03-17 09:30:34