2014-09-21 21 views
31

Tôi đang cố gắng triển khai thanh tiến trình để cho biết tiến trình tải tệp lên nhiều phần.Theo dõi tiến trình tải lên tệp nhiều phần bằng cách sử dụng OKHTTP

Tôi đã đọc từ nhận xét về câu trả lời này - https://stackoverflow.com/a/24285633/1022454 mà tôi phải bọc bồn rửa được chuyển đến RequestBody và cung cấp một cuộc gọi lại theo dõi các byte được di chuyển.

Tôi đã tạo một RequestBody tùy chỉnh và bao bọc bồn rửa bằng lớp CustomSink, tuy nhiên thông qua gỡ lỗi tôi có thể thấy các byte đang được viết bởi RealBufferedSink ln 44 và phương thức ghi chìm tùy chỉnh chỉ chạy một lần, không cho phép tôi theo dõi các byte được di chuyển.

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(); 
    } 
} 

Có ai có ví dụ về cách tôi thực hiện việc này không?

Trả lời

54

Bạn phải tạo một RequestBody tùy chỉnh và ghi đè phương pháp writeTo và bạn phải gửi các tệp của mình xuống bồn rửa trong phân đoạn S. Điều quan trọng là bạn xả bồn rửa sau mỗi phân đoạn, nếu không thanh tiến trình của bạn sẽ lấp đầy nhanh chóng mà không có tệp thực sự được gửi qua mạng, bởi vì nội dung sẽ nằm trong bồn rửa (hoạt động như bộ đệm).

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); 
    } 

} 

Bạn có thể tìm một việc thực hiện hoàn chỉnh hỗ trợ hiển thị tiến bộ trong một AdapterView và cũng hủy cập nhật tại ý chính của tôi: https://gist.github.com/eduardb/dd2dc530afd37108e1ac

+0

này dường như không có tác dụng nếu tải file nhỏ qua kết nối mạng chậm. Xem https://github.com/square/okhttp/issues/1078. Có một giải pháp cho trường hợp này? – coalmee

+0

@Edy Bolos Có cách nào để sử dụng nó cùng với RxJava & Observable không? – Sree

+0

Để trả lời câu hỏi của bạn: mọi thứ có thể được bao bọc trong một Observable :) Nhưng tôi sẽ phải để người khác làm điều đó. Đề nghị duy nhất của tôi là có thể sử dụng một 'BehaviorSubject' để phát ra giá trị tiến trình trong' UploadsHandler' –

10
  • Chúng tôi chỉ cần tạo một tùy chỉnh RequestBody, không cần triển khai tùy chỉnh BufferedSink. Chúng tôi có thể cấp phát bộ đệm Okio để đọc từ tệp hình ảnh và kết nối bộ đệm này để chìm.

Đối với một ví dụ, xin vui lòng xem chức năng dưới đây 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(); 
      } 
     } 
    }; 
} 
  • sử dụng -

    .addPart(
        Headers.of("Content-Disposition", "form-data; name=\"image\""), 
        createCustomRequestBody(MediaType.parse("image/png"), new File("test.jpg"))) 
    .build() 
    
1

điều này hoạt động tuyệt vời!

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 mã ví dụ ở đây https://github.com/lizhangqu/CoreProgress

Các vấn đề liên quan