2014-09-21 1 views
31

멀티 파트 파일 업로드 진행률을 나타내는 진행률 표시 줄을 구현하려고합니다.OKHTTP를 사용하여 멀티 파트 파일 업로드의 진행 상황을 추적

나는이 응답에 대한 의견 - https://stackoverflow.com/a/24285633/1022454을 읽고 RequestBody로 전달 된 싱크를 래핑하고 이동 된 바이트를 추적하는 콜백을 제공해야합니다.

사용자 정의 RequestBody를 작성하고 싱크를 CustomSink 클래스로 랩핑했지만 디버깅을 통해 바이트가 RealBufferedSink11n으로 작성되고 사용자 정의 싱크 쓰기 메소드가 한 번만 실행되어 나를 허용하지 않는다는 것을 알 수 있습니다. 이동 된 바이트를 추적하십시오.

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

아무도이 작업을 수행하는 방법에 대한 예제가 있습니까?

답변

54

을 당신은 정의 RequestBody을 만들고는, writeTo 메소드를 오버라이드 (override) 할 필요가 , 거기에 세그먼트의 싱크대 아래로 파일을 보내야합니다. 에스. 각 세그먼트 후에 싱크를 비우는 것이 매우 중요합니다. 그렇지 않으면 콘텐츠가 싱크 (버퍼와 같은 역할을하기 때문에)에 남아 있기 때문에 파일이 네트워크를 통해 실제로 전송되지 않고 진행률 표시 줄이 빠르게 채워집니다.

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

이 느린 네트워크 연결을 통해 작은 파일을 업로드 할 경우 작동하지 않습니다. https://github.com/square/okhttp/issues/1078을 참조하십시오. 이 사건에 대한 해결책이 있습니까? – coalmee

+0

@Edy Bolos RxJava & Observable과 함께 사용하는 방법이 있습니까? – Sree

+0

질문에 대답하려면 : Observable로 모든 것을 감쌀 수 있습니다. :)하지만 다른 사람에게 맡겨야합니다. 나의 유일한 제안은'UploadsHandler'에서 진도 값을 출력하기 위해'BehaviorSubject'를 사용하는 것일 것입니다. –

10
  • 사용자 정의 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() 
    
1

이 일이 잘 작동합니다!

Gradle을

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/lizhangqu/CoreProgress

관련 문제