2014-09-21 12 views
31

Sto tentando di implementare una barra di avanzamento per indicare lo stato di avanzamento di un caricamento di file multipart.Monitoraggio dell'avanzamento del caricamento di file multipart utilizzando OKHTTP

Ho letto da un commento su questa risposta - https://stackoverflow.com/a/24285633/1022454 che devo avvolgere il sink passato al RequestBody e fornire un callback che tiene traccia dei byte spostati.

Ho creato un RequestBody personalizzato e ho avvolto il sink con una classe CustomSink, tuttavia tramite debug posso vedere che i byte vengono scritti da RealBufferedSink ln 44 e il metodo di scrittura del sink personalizzato viene eseguito una sola volta, non permettendomi di traccia i byte spostati.

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

Qualcuno ha un esempio di come farei per farlo?

risposta

54

È necessario creare un RequestBody personalizzato e sovrascrivere il metodo WriteTo e qui devi inviare i tuoi file nel lavandino nel segmento S. È molto importante lavare il lavandino dopo ogni segmento, altrimenti la barra di avanzamento si riempirà rapidamente senza che il file venga effettivamente inviato sulla rete, poiché il contenuto rimarrà nel sink (che funge da buffer).

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

} 

È possibile trovare un'implementazione completa che supporta la visualizzazione di progresso in un AdapterView e anche l'annullamento arrivi al mio gist: https://gist.github.com/eduardb/dd2dc530afd37108e1ac

+0

Questo non sembra funzionare se il caricamento di file di piccole dimensioni su una connessione di rete lenta. Vedi https://github.com/square/okhttp/issues/1078. C'è una soluzione per questo caso? – coalmee

+0

@Edy Bolos C'è un modo per usarlo in combinazione con RxJava & Observable? – Sree

+0

Per rispondere alla tua domanda: tutto può essere avvolto in un Osservabile :) Ma dovrò lasciare qualcun altro a farlo. Il mio unico suggerimento è quello di usare forse un "BehaviorSubject" per emettere il valore di avanzamento in "UploadsHandler" –

10
  • Abbiamo solo bisogno di creare un RequestBody personalizzato, non è necessario implementare personalizzato BufferedSink. Possiamo allocare il buffer Okio per leggere dal file immagine e collegare questo buffer per affondare.

Per un esempio, vedere la funzione qui sotto 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(); 
      } 
     } 
    }; 
} 
  • da usare -

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

Questa cosa funziona benissimo!

Gradle

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

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

codice di esempio completo qui https://github.com/lizhangqu/CoreProgress

Problemi correlati