Downloading a file using retrofit
I've started using retrofit recently and it's intuitively simpler than Google's volley. And it's got really good benchmarks. And it's made by the same guys who made Dagger and Picasso. So you should give it a try too if you haven't.
I spent considerable amount of time figuring how to download an image yesterday. So here's how to do it using retrofit. This will work for any file type - not just images.
Create the service interface
public interface IMyService {
@GET
@Streaming
Call<ResponseBody> getFile(@Url String url);
}
Set permissions
The app will require the following permissions:
INTERNET
- to access the internet & download the fileWRITE_EXTERNAL_STORAGE
- to be able to write to the filesystem & save the file
Add the following code before opening the <application>
in the AndroidManifest.xml file:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Create the AsyncTask
Since I'm downloading an image, I'm going to save the file in a folder named after the app in the pictures directory.
private class DownloadFileAsyncTask extends AsyncTask<InputStream, Void, Boolean> {
final String appDirectoryName = "AppName";
final File imageRoot = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), appDirectoryName);
final String filename = "image.jpg";
@Override
protected Boolean doInBackground(InputStream... params) {
InputStream inputStream = params[0];
File file = new File(imageRoot, filename);
OutputStream output = null;
try {
output = new FileOutputStream(file);
byte[] buffer = new byte[1024]; // or other buffer size
int read;
Log.d(TAG, "Attempting to write to: " + imageRoot + "/" + filename);
while ((read = inputStream.read(buffer)) != -1) {
output.write(buffer, 0, read);
Log.v(TAG, "Writing to buffer to output stream.");
}
Log.d(TAG, "Flushing output stream.");
output.flush();
Log.d(TAG, "Output flushed.");
} catch (IOException e) {
Log.e(TAG, "IO Exception: " + e.getMessage());
e.printStackTrace();
return false;
} finally {
try {
if (output != null) {
output.close();
Log.d(TAG, "Output stream closed sucessfully.");
}
else{
Log.d(TAG, "Output stream is null");
}
} catch (IOException e){
Log.e(TAG, "Couldn't close output stream: " + e.getMessage());
e.printStackTrace();
return false;
}
}
return true;
}
@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
Log.d(TAG, "Download success: " + result);
// TODO: show a snackbar or a toast
}
}
In my case, the class is an inner class. TAG
is defined in the outer class as:
private static final String TAG = "###ActivityName";
Build an instance of the Service
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.base.url")
.build();
IMyService service = retrofit.create(IMyService.class);
The base url is required. The url doesn't matter if you're going to be using an absolute url to download the file. Relative urls must however be relative to the base url.
Initiate the download
service.getFile("https://image.url/goes/here).enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
Log.d(TAG, response.message());
if(!response.isSuccess()){
Log.e(TAG, "Something's gone wrong");
// TODO: show error message
return;
}
DownloadFileAsyncTask downloadFileAsyncTask = new DownloadFileAsyncTask();
downloadFileAsyncTask.execute(response.body().byteStream());
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.e(TAG, t.getMessage());
// TODO: show error message
}
});