Download binary file from OKHTTP

pratsJ picture pratsJ · Sep 17, 2014 · Viewed 74.2k times · Source

I am using OKHTTP client for networking in my android application.

This example shows how to upload binary file. I would like to know how to get inputstream of binary file downloading with OKHTTP client.

Here is the listing of the example :

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

Current code for simple get request is:

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

Now how do I convert the response to InputStream. Something similar to response from Apache HTTP Client like this for OkHttp response:

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

EDIT

Accepted answer from below. My modified code:

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

Answer

kiddouk picture kiddouk · Mar 12, 2015

For what it's worth, I would recommend response.body().source() from okio (since OkHttp is already supporting it natively) in order to enjoy an easier way to manipulate a large quantity of data that can come when downloading a file.

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

A couple of advantages taken from the documentation in comparison with InputStream:

This interface is functionally equivalent to InputStream. InputStream requires multiple layers when consumed data is heterogeneous: a DataInputStream for primitive values, a BufferedInputStream for buffering, and InputStreamReader for strings. This class uses BufferedSource for all of the above. Source avoids the impossible-to-implement available() method. Instead callers specify how many bytes they require.

Source omits the unsafe-to-compose mark and reset state that's tracked by InputStream; callers instead just buffer what they need.

When implementing a source, you need not worry about the single-byte read method that is awkward to implement efficiently and that returns one of 257 possible values.

And source has a stronger skip method: BufferedSource.skip(long) won't return prematurely.