Android 使用OKHTTP跟踪多部分文件上载的进度

Android 使用OKHTTP跟踪多部分文件上载的进度,android,okhttp,okio,Android,Okhttp,Okio,我试图实现一个进度条来指示多部分文件上传的进度 我从对这个答案的评论中读到,我必须包装传递给RequestBody的接收器,并提供一个跟踪移动字节的回调 我已经创建了一个自定义RequestBody并用CustomSink类包装了接收器,但是通过调试,我可以看到LN44正在写入字节,并且自定义接收器写入方法只运行一次,不允许我跟踪移动的字节 private class CustomRequestBody extends RequestBody { MediaType conte

我试图实现一个进度条来指示多部分文件上传的进度

我从对这个答案的评论中读到,我必须包装传递给RequestBody的接收器,并提供一个跟踪移动字节的回调

我已经创建了一个自定义RequestBody并用CustomSink类包装了接收器,但是通过调试,我可以看到LN44正在写入字节,并且自定义接收器写入方法只运行一次,不允许我跟踪移动的字节

    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
    ,不需要实现定制的
    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()
    

您必须创建一个自定义的RequestBody并重写writeTo方法,在那里您必须将文件分段发送到接收器。在每个段之后刷新接收器非常重要,否则您的进度条将很快填满,而不会通过网络实际发送文件,因为内容将保留在接收器中(其作用类似于缓冲区)


您可以找到一个完整的实现,它支持在AdapterView中显示进度,还可以根据我的要点取消上载:

这件事非常有效

格拉德尔

dependencies {
  compile 'io.github.lizhangqu:coreprogress:1.0.2'
}

//wrap your original request body with progress
RequestBody requestBody = ProgressHelper.withProgress(body, new ProgressUIListener()....} 
这里有完整的示例代码

如果通过缓慢的网络连接上载小文件,则这似乎不起作用。看见有没有解决这个问题的方法?@Edy Bolos有没有办法将它与RxJava&Observable结合使用?回答你的问题:一切都可以包装在Observable中:),但我必须让其他人来做。我唯一的建议是使用
BehaviorSubject
UploadsHandler
中发出进度值,正如前面的评论员所说的那样。即使在连接不好的情况下,小文件也不会取得进展。这是关于网络接口发送缓冲区如此之高,以至于吞没了整个文件,okHttp将报告它100%上传。使用此选项可以修复此问题。你们能定义一下你们所说的“小”是指10MB还是2MB,或者是什么…?这不会像预期的那样起作用,原因在文章中解释
在每个段之后刷新接收器非常重要,否则进度条将很快填满,而文件不会通过网络实际发送
dependencies {
  compile 'io.github.lizhangqu:coreprogress:1.0.2'
}

//wrap your original request body with progress
RequestBody requestBody = ProgressHelper.withProgress(body, new ProgressUIListener()....}