Seguimiento del progreso de la carga de archivos multipart usando OKHTTP

Estoy intentando implementar una barra de progreso para indicar el progreso de una subida de archivos multipart.

He leído de un comentario sobre esta respuesta – https://stackoverflow.com/a/24285633/1022454 que tengo que envolver el fregadero pasado a RequestBody y proporcionar una devolución de llamada que rastrea los bytes movidos.

He creado un RequestBody personalizado y envuelto el fregadero con una clase de CustomSink, sin embargo a través de la depuración puedo ver que los bytes están siendo escritos por RealBufferedSink ln 44 y el método de escritura de fregadero personalizado sólo se ejecuta una vez, no me permite rastrear los bytes movidos .

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

¿Alguien tiene un ejemplo de cómo iba a hacer esto?

Tienes que crear un método RequestBody personalizado y anular writeTo, y allí tienes que enviar tus archivos por el fregadero en segmentos. Es muy importante que vacíe el fregadero después de cada segmento, de lo contrario su barra de progreso se llenará rápidamente sin que el archivo se envíe realmente a través de la red, porque el contenido permanecerá en el fregadero (que actúa como un búfer).

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

Puede encontrar una implementación completa que admita la visualización de progreso en un AdapterView y también la cancelación de subidas en mi parte esencial: https://gist.github.com/eduardb/dd2dc530afd37108e1ac

  • Sólo necesitamos crear un RequestBody personalizado, sin necesidad de implementar BufferedSink personalizado. Podemos asignar el búfer de Okio a leer del archivo de la imagen, y conectar este almacenador intermediario al fregadero.

Para obtener un ejemplo, consulte la siguiente función 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(); } } }; } 
  • usar –

     .addPart( Headers.of("Content-Disposition", "form-data; name=\"image\""), createCustomRequestBody(MediaType.parse("image/png"), new File("test.jpg"))) .build() 
FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.