Descargar archivo binario desde OKHTTP

Resuelto pratsJ asked hace 54 años • 11 respuestas

Estoy usando el cliente OKHTTP para establecer redes en mi aplicación de Android.

Este ejemplo muestra cómo cargar un archivo binario. Me gustaría saber cómo obtener el flujo de entrada de la descarga de archivos binarios con el cliente OKHTTP.

Aquí está la lista del ejemplo:

public class InputStreamRequestBody extends RequestBody {

    private InputStream inputStream;
    private MediaType mediaType;

    public static RequestBody create(final MediaType mediaType, 
            final InputStream inputStream) {
        return new InputStreamRequestBody(inputStream, mediaType);
    }

    private InputStreamRequestBody(InputStream inputStream, MediaType mediaType) {
        this.inputStream = inputStream;
        this.mediaType = mediaType;
    }

    @Override
    public MediaType contentType() {
        return mediaType;
    }

    @Override
    public long contentLength() {
        try {
            return inputStream.available();
        } catch (IOException e) {
            return 0;
        }
    }

    @Override
    public void writeTo(BufferedSink sink) throws IOException {
        Source source = null;
        try {
            source = Okio.source(inputStream);
            sink.writeAll(source);
        } finally {
            Util.closeQuietly(source);
        }
    }
}

El código actual para una solicitud de obtención simple es:

OkHttpClient client = new OkHttpClient();
request = new Request.Builder().url("URL string here")
                    .addHeader("X-CSRFToken", csrftoken)
                    .addHeader("Content-Type", "application/json")
                    .build();
response = getClient().newCall(request).execute();

Ahora, ¿cómo convierto la respuesta a InputStream. Algo similar a una respuesta Apache HTTP Clientcomo esta para OkHttpla respuesta:

InputStream is = response.getEntity().getContent();

EDITAR

Respuesta aceptada desde abajo. Mi código modificado:

request = new Request.Builder().url(urlString).build();
response = getClient().newCall(request).execute();

InputStream is = response.body().byteStream();

BufferedInputStream input = new BufferedInputStream(is);
OutputStream output = new FileOutputStream(file);

byte[] data = new byte[1024];

long total = 0;

while ((count = input.read(data)) != -1) {
    total += count;
    output.write(data, 0, count);
}

output.flush();
output.close();
input.close();
pratsJ avatar Jan 01 '70 08:01 pratsJ
Aceptado

response.body().source()Por si sirve de algo, recomendaría Okio (ya que OkHttp ya lo admite de forma nativa) para disfrutar de una forma más sencilla de manipular una gran cantidad de datos que pueden surgir al descargar un archivo.

@Override
public void onResponse(Call call, Response response) throws IOException {
    File downloadedFile = new File(context.getCacheDir(), filename);
    BufferedSink sink = Okio.buffer(Okio.sink(downloadedFile));
    sink.writeAll(response.body().source());
    sink.close();
}

Un par de ventajas extraídas de la documentación en comparación con InputStream:

Esta interfaz es funcionalmente equivalente a InputStream. InputStream requiere varias capas cuando los datos consumidos son heterogéneos: un DataInputStream para valores primitivos, un BufferedInputStream para almacenamiento en búfer y un InputStreamReader para cadenas. Esta clase usa BufferedSource para todo lo anterior. La fuente evita el método disponible() imposible de implementar. En lugar de ello, los llamantes especifican cuántos bytes necesitan.

La fuente omite la marca de no seguro para componer y el estado de reinicio que realiza el seguimiento de InputStream; en cambio, las personas que llaman simplemente almacenan lo que necesitan.

Al implementar una fuente, no necesita preocuparse por el método de lectura de un solo byte que es incómodo de implementar de manera eficiente y que devuelve uno de los 257 valores posibles.

Y la fuente tiene un método de omisión más potente: BufferedSource.skip(long) no regresará prematuramente.

kiddouk avatar Mar 12 '2015 14:03 kiddouk

Obteniendo ByteStream de OKHTTP

He estado investigando en la documentación de OkHttp , debes ir por este camino.

utiliza este método:

Response.body().byteStream() que devolverá un InputStream

entonces puedes simplemente usar un BufferedReader o cualquier otra alternativa

OkHttpClient client = new OkHttpClient();
request = new Request.Builder().url("URL string here")
                     .addHeader("X-CSRFToken", csrftoken)
                     .addHeader("Content-Type", "application/json")
                     .build();
response = getClient().newCall(request).execute();

InputStream in = response.body().byteStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String result, line = reader.readLine();
result = line;
while((line = reader.readLine()) != null) {
    result += line;
}
System.out.println(result);
response.body().close();
Nadir Belhaj avatar Sep 17 '2014 14:09 Nadir Belhaj

La mejor opción para descargar (basada en el código fuente "okio")

private void download(@NonNull String url, @NonNull File destFile) throws IOException {
    Request request = new Request.Builder().url(url).build();
    Response response = okHttpClient.newCall(request).execute();
    ResponseBody body = response.body();
    long contentLength = body.contentLength();
    BufferedSource source = body.source();

    BufferedSink sink = Okio.buffer(Okio.sink(destFile));
    Buffer sinkBuffer = sink.buffer();

    long totalBytesRead = 0;
    int bufferSize = 8 * 1024;
    for (long bytesRead; (bytesRead = source.read(sinkBuffer, bufferSize)) != -1; ) {
        sink.emit();
        totalBytesRead += bytesRead;
        int progress = (int) ((totalBytesRead * 100) / contentLength);
        publishProgress(progress);
    }
    sink.flush();
    sink.close();
    source.close();
}
e.shishkin avatar Apr 13 '2016 09:04 e.shishkin

Así es como uso las bibliotecas Okhttp + Okio mientras publico el progreso de la descarga después de cada descarga parcial:

public static final int DOWNLOAD_CHUNK_SIZE = 2048; //Same as Okio Segment.SIZE

try {
        Request request = new Request.Builder().url(uri.toString()).build();

        Response response = client.newCall(request).execute();
        ResponseBody body = response.body();
        long contentLength = body.contentLength();
        BufferedSource source = body.source();

        File file = new File(getDownloadPathFrom(uri));
        BufferedSink sink = Okio.buffer(Okio.sink(file));

        long totalRead = 0;
        long read = 0;
        while ((read = source.read(sink.buffer(), DOWNLOAD_CHUNK_SIZE)) != -1) {
            totalRead += read;
            int progress = (int) ((totalRead * 100) / contentLength);
            publishProgress(progress);
        }
        sink.writeAll(source);
        sink.flush();
        sink.close();
        publishProgress(FileInfo.FULL);
} catch (IOException e) {
        publishProgress(FileInfo.CODE_DOWNLOAD_ERROR);
        Logger.reportException(e);
}
Shubham Chaudhary avatar Dec 17 '2015 21:12 Shubham Chaudhary