使用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

+0

摇篮

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

完整的示例代码,这似乎并不因上载小文件通过速度较慢的网络连接工作。请参阅https://github.com/square/okhttp/issues/1078。这种情况是否有解决方案? – coalmee 2015-11-13 23:03:11

+0

@Edy Bolos有没有办法将它与RxJava&Observable结合使用? – Sree 2016-03-16 19:53:46

+0

回答你的问题:一切都可以包裹在一个可观察的:)但我必须离开别人去做。我唯一的建议是可能使用'BehaviorSubject'在'UploadsHandler'中发送进度值。 – 2016-03-17 09:30:34

  • 我们只需要创建一个自定义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/lizhangqu/CoreProgress