2014-09-21 9 views
31

Ich versuche, eine Fortschrittsanzeige zu implementieren, um den Fortschritt eines mehrteiligen Dateiuploads anzuzeigen.Verfolgen des Fortschritts des mehrteiligen Dateiuploads mit OKHTTP

Ich habe aus einem Kommentar zu dieser Antwort gelesen - https://stackoverflow.com/a/24285633/1022454, dass ich die Senke um den RequestBody übergeben muss und einen Rückruf bereitstellen, der die bewegten Bytes verfolgt.

Ich habe eine benutzerdefinierte RequestBody erstellt und die Senke mit einer CustomSink-Klasse umwickelt, aber durch Debuggen kann ich sehen, dass die Bytes geschrieben werden von RealBufferedSink ln 44 und die benutzerdefinierte Senke Schreibmethode wird nur einmal ausgeführt, mir nicht erlaubt verfolgen Sie die bewegten Bytes.

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

Hat jemand ein Beispiel dafür, wie ich das machen würde?

Antwort

54

Sie haben eine benutzerdefinierte RequestBody erstellen und WriteTo Methode überschreiben , und dort müssen Sie Ihre Dateien im Segment in die Senke schicken s. Es ist sehr wichtig, dass Sie die Senke nach jedem Segment leeren, sonst füllt sich Ihr Fortschrittsbalken schnell, ohne dass die Datei tatsächlich über das Netzwerk gesendet wird, da der Inhalt in der Senke verbleibt (die wie ein Puffer wirkt).

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

} 

Sie eine vollständige Implementierung finden können, die in einem AdapterView unterstützt und auch Cancelling Uploads an meinem Kern Anzeigen Fortschritt: https://gist.github.com/eduardb/dd2dc530afd37108e1ac

+0

Dies scheint nicht zu funktionieren, wenn kleine Dateien über eine langsame Netzwerkverbindung hochladen. Siehe https://github.com/square/okhttp/issues/1078. Gibt es eine Lösung für diesen Fall? – coalmee

+0

@Edy Bolos Gibt es eine Möglichkeit, es in Verbindung mit RxJava & Observable zu verwenden? – Sree

+0

Um Ihre Frage zu beantworten: alles kann in einem Observable verpackt werden :) Aber ich muss jemand anderen lassen, um es zu tun. Mein einziger Vorschlag ist vielleicht, ein "BehaviorSubject" zu verwenden, um den Fortschrittswert in "UploadsHandler" zu emittieren. –

10
  • Wir müssen nur eine benutzerdefinierte RequestBody, keine Notwendigkeit erstellen individuelle BufferedSink zu implementieren. Wir können Okio-Puffer zuweisen, um aus Bilddatei zu lesen, und diesen Puffer mit Senke verbinden.

Ein Beispiel der

unter createCustomRequestBody Funktion finden Sie in
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(); 
      } 
     } 
    }; 
} 
  • verwenden -

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

Dieses Ding funktioniert super!

Gradle

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

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

Vollbeispielcode hier https://github.com/lizhangqu/CoreProgress

Verwandte Themen