49

I need to download all types of file (binary, image, text, etc) using Retrofit library in my app. All the examples on the net is using HTML GET method. I need to use POST to prevent automatic caching.

My question is how to download a file using POST method in Retrofit?

NaviRamyle
  • 3,967
  • 1
  • 31
  • 49
Dr. Ehsan Ali
  • 4,735
  • 4
  • 23
  • 37

9 Answers9

49

In kotlin, Do this:

In your service add method:

    @Streaming
    @GET
    suspend fun downloadFile(@Url fileUrl:String): Response<ResponseBody>

To call this method, from ViewModel:

viewModelScope.launch {
     val responseBody=yourServiceInstance.downloadFile(url).body()
     saveFile(responseBody,pathWhereYouWantToSaveFile)
}

To save file:

fun saveFile(body: ResponseBody?, pathWhereYouWantToSaveFile: String):String{
        if (body==null)
            return ""
        var input: InputStream? = null
        try {
            input = body.byteStream()
            //val file = File(getCacheDir(), "cacheFileAppeal.srl")
            val fos = FileOutputStream(pathWhereYouWantToSaveFile)
            fos.use { output ->
                val buffer = ByteArray(4 * 1024) // or other buffer size
                var read: Int
                while (input.read(buffer).also { read = it } != -1) {
                    output.write(buffer, 0, read)
                }
                output.flush()
            }
            return pathWhereYouWantToSaveFile
        }catch (e:Exception){
            Log.e("saveFile",e.toString())
        }
        finally {
            input?.close()
        }
        return ""
    }

Note:

  1. Make sure your refrofit client's base url and the url passed to downloadFile makes valid file url:

Retrofit's Base url + downloadFile's method url = File url

  1. Here I am using suspend keyword before downloadFile to call this from ViewModel, I have used viewModelScope.launch {} you can use different coroutine scope according to your caller end.

  2. Now pathWhereYouWantToSaveFile, If you want to store file into project's file directory, you can do this:

val fileName=url.substring(url.lastIndexOf("/")+1)
val pathWhereYouWantToSaveFile = myApplication.filesDir.absolutePath+fileName
  1. If you are storing the downloaded file under file or cache directory, you don't need to acquire permission, otherwise for public storage, you know the process.
Suraj Vaishnav
  • 7,777
  • 4
  • 43
  • 46
  • " Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $" -> this is the error I have got – Mostafa Imani May 23 '22 at 05:52
  • 1
    after adding GsonBuilder() .setLenient() .create(); and add it to addConverterFactory() ifaced new error that Respone body has no argument – Mostafa Imani May 25 '22 at 04:19
19

Use @Streaming

Asynchronous

EDIT 1

//On your api interface
@POST("path/to/your/resource")
@Streaming
void apiRequest(Callback<POJO> callback);

restAdapter.apiRequest(new Callback<POJO>() {
        @Override
        public void success(POJO pojo, Response response) {
            try {
                //you can now get your file in the InputStream
                InputStream is = response.getBody().in();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void failure(RetrofitError error) {

        }
    });

Synchronous

//On your api interface
@POST("path/to/your/resource")
@Streaming
Response apiRequest();

Response response = restAdapter.apiRequest();

try {
    //you can now get your file in the InputStream
    InputStream is = response.getBody().in();
} catch (IOException e) {
    e.printStackTrace();
}
NaviRamyle
  • 3,967
  • 1
  • 31
  • 49
10

This is How to DOWNLOAD file in Retrofit 2

public interface ServerAPI {
        @GET
        Call<ResponseBody> downlload(@Url String fileUrl);

        Retrofit retrofit =
                new Retrofit.Builder()
                        .baseUrl("http://192.168.43.135/retro/") // REMEMBER TO END with /
                        .addConverterFactory(GsonConverterFactory.create())
                 .build();

}

    //How To Call
public void download(){
        ServerAPI api = ServerAPI.retrofit.create(ServerAPI.class);
        api.downlload("http://192.168.43.135/retro/pic.jpg").enqueue(new Callback<ResponseBody>() {
                    @Override
                    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                        try {
                            File path = Environment.getExternalStorageDirectory();
                            File file = new File(path, "file_name.jpg");
                            FileOutputStream fileOutputStream = new FileOutputStream(file);
                            IOUtils.write(response.body().bytes(), fileOutputStream);
                        }
                        catch (Exception ex){
                        }
                    }

                    @Override
                    public void onFailure(Call<ResponseBody> call, Throwable t) {
                    }
                });
}
ugali soft
  • 2,719
  • 28
  • 25
10

With Kotlin, it's little bit simple.

API Service

@GET
@Streaming
fun download(@Url url: String): Call<ResponseBody>

API Client

object ApiClient {
    private val retrofit = ...

    val service: ApiService = retrofit.create(ApiService::class.java)
}

Download Function

fun download(urlString: String, target: File) {
    val response = ApiClient.service.download(urlString).execute()
    response.body()?.byteStream()?.use {
        target.parentFile?.mkdirs()
    
        FileOutputStream(target).use { targetOutputStream ->
            it.copyTo(targetOutputStream)
        }
    } ?: throw RuntimeException("failed to download: $urlString")
}
khcpietro
  • 1,459
  • 2
  • 23
  • 42
3

If you use Retrofit 2.0.0, you can refer my answer under the question -- Use retrofit to download image file.

The key point is use okhttp3.ReponseBody to receive the raw binary data, not any POJO.

And you want to use POST method to get the file, it's easy, just change @GET to @POST, but it depend whether your server support the POST method!

Community
  • 1
  • 1
Spark.Bao
  • 5,573
  • 2
  • 31
  • 36
3

You can use below code for downloading with progress (Kotlin)

Retrofit Api Service

@Streaming
@GET
fun downloadFile(@Url fileUrl: String): Observable<Response<ResponseBody>>

make sure you add @Streaming for large file downloading

And paste below code in your Activity or Fragment

fun downloadfileFromRetrofit() {
    val retrofit = Retrofit.Builder()
        .baseUrl("ENTER_YOUR_BASE_URL")
        .client(OkHttpClient.Builder().build())
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create()).build()
    val downloadService = retrofit.create(RetrofitApi::class.java)

   downloadService.downloadFile("FILE_URL_PATH").observeOn(AndroidSchedulers.mainThread())
        .subscribeOn(Schedulers.io()).subscribe({
            val task = object : AsyncTask<Void, Integer, Void>() {
                override fun doInBackground(vararg voids: Void): Void? {
                    val writtenToDisk =writeResponseBodyToDisk(it.body()!!)
                    println("file download was a success? $writtenToDisk")
                    return null
                }
            }
            task.execute()
        }, {
            print(it.message)
        })
}

below is the writeResponseBodyToDisk method

fun writeResponseBodyToDisk(body: ResponseBody): Boolean {
    val appDirectoryName = "YOUR_DIRECTORY_NAME"
    val filename = "YOUR_FILE_NAME"
    val apkFile = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), filename)
    try {

        var inputStream: InputStream? = null
        var outputStream: OutputStream? = null
        try {
            val fileReader = ByteArray(4096)
            val fileSize = body.contentLength()
            var fileSizeDownloaded: Long = 0
            inputStream = body.byteStream()
            outputStream = FileOutputStream(apkFile)
            while (true) {
                val read = inputStream!!.read(fileReader)
                if (read == -1) {
                    break
                }
                outputStream.write(fileReader, 0, read)
                fileSizeDownloaded += read.toLong()

           calulateProgress(fileSize.toDouble(),fileSizeDownloaded.toDouble()
                println("file downloading $fileSizeDownloaded of $fileSize")

            outputStream.flush()

            return true
        } catch (e: Exception) {
            println(e.toString())
            return false
        } finally {
            if (inputStream != null) {
                inputStream!!.close()
            }
            outputStream?.close()
        }
    } catch (e: Exception) {
        println(e.toString())
        return false
    }

}

below method is for calculate progress

 fun calulateProgress(totalSize:Double,downloadSize:Double):Double{
    return ((downloadSize/totalSize)*100)
}
CoolMind
  • 26,736
  • 15
  • 188
  • 224
Shahid Khan
  • 371
  • 2
  • 10
2

I used the following code for downloading the any type of file using retrofit ...

 File file = new File("Your_File_path/name");

  private void startDownload() {

    if (!NetWorkUtils.getInstance(context).isNetworkAvailable()) {
        Toast.makeText(context, "No data connection available", Toast.LENGTH_SHORT).show();
        return;
    }

    showProgressDialog();

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(FILE_BASE_URL)
            .build();

    FileHandlerService handlerService = retrofit.create(FileHandlerService.class);

    Call<ResponseBody> call = handlerService.downloadFile(mFileName);
    call.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
            dismissProgressDialog();
            if (response.isSuccessful()) {
                if (writeResponseBodyToDisk(response.body())) {
                    listener.onFileLoaded(file);
                }
            } else {
                listener.onDownloadFailed("Resource not Found");
            }
        }

        @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {
            dismissProgressDialog();
            listener.onDownloadFailed("Download Failed");
            t.printStackTrace();
        }
    });

}


interface FileHandlerService {

    @GET("uploads/documents/{file_name}")
    Call<ResponseBody> downloadFile(
            @Path("file_name") String imageName);
}

private boolean writeResponseBodyToDisk(ResponseBody body) {
    try {

        InputStream inputStream = null;
        OutputStream outputStream = null;

        try {
            byte[] fileReader = new byte[4096];

            long fileSize = body.contentLength();
            long fileSizeDownloaded = 0;

            inputStream = body.byteStream();
            outputStream = new FileOutputStream(file);

            while (true) {
                int read = inputStream.read(fileReader);

                if (read == -1) {
                    break;
                }

                outputStream.write(fileReader, 0, read);

                fileSizeDownloaded += read;

                Log.d(TAG, "file download: " + fileSizeDownloaded + " of " + fileSize);
            }

            outputStream.flush();

            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
            if (outputStream != null) {
                outputStream.close();
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
        return false;
    }
}
Ameen Maheen
  • 2,719
  • 1
  • 28
  • 28
1

Include following function in MainActivity.java:

void getRetrofitImage() {

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(url)
            .addConverterFactory(GsonConverterFactory.create())
            .build();

    RetrofitImageAPI service = retrofit.create(RetrofitImageAPI.class);

    Call<ResponseBody> call = service.getImageDetails();

    call.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Response<ResponseBody> response, Retrofit retrofit) {

            try {

                Log.d("onResponse", "Response came from server");

                boolean FileDownloaded = DownloadImage(response.body());

                Log.d("onResponse", "Image is downloaded and saved ? " + FileDownloaded);

            } catch (Exception e) {
                Log.d("onResponse", "There is an error");
                e.printStackTrace();
            }

        }

        @Override
        public void onFailure(Throwable t) {
            Log.d("onFailure", t.toString());
        }
    });
}

File handling part of Image download will be:

private boolean DownloadImage(ResponseBody body) {

    try {
        Log.d("DownloadImage", "Reading and writing file");
        InputStream in = null;
        FileOutputStream out = null;

        try {
            in = body.byteStream();
            out = new FileOutputStream(getExternalFilesDir(null) + File.separator + "AndroidTutorialPoint.jpg");
            int c;

            while ((c = in.read()) != -1) {
                out.write(c);
            }
        }
        catch (IOException e) {
            Log.d("DownloadImage",e.toString());
            return false;
        }
        finally {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
        }

        int width, height;
        ImageView image = (ImageView) findViewById(R.id.imageViewId);
        Bitmap bMap = BitmapFactory.decodeFile(getExternalFilesDir(null) + File.separator + "AndroidTutorialPoint.jpg");
        width = 2*bMap.getWidth();
        height = 6*bMap.getHeight();
        Bitmap bMap2 = Bitmap.createScaledBitmap(bMap, width, height, false);
        image.setImageBitmap(bMap2);

        return true;

    } catch (IOException e) {
        Log.d("DownloadImage",e.toString());
        return false;
    }
}

You can see this for full tutorial: Image Download using Retrofit 2.0

Navneet Goel
  • 116
  • 6
0

The request declaration for downloading files looks like this given below

// option 1: a resource relative to your base URL
@GET("/resource/example.zip")
Call<ResponseBody> downloadFileWithFixedUrl();

// option 2: using a dynamic URL
@GET
Call<ResponseBody> downloadFileWithDynamicUrlSync(@Url String fileUrl);  

After declaring your request call like this

FileDownloadService downloadService = ServiceGenerator.create(FileDownloadService.class);

Call<ResponseBody> call = downloadService.downloadFileWithDynamicUrlSync(fileUrl);

call.enqueue(new Callback<ResponseBody>() {  
    @Override
    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
        if (response.isSuccess()) {
            Log.d(TAG, "server contacted and has file");

            boolean writeToDisk = writeToDisk(response.body());

            Log.d(TAG, "file downloaded " + writtenToDisk);
        } else {
            Log.d(TAG, "server error");
        }
    }

    @Override
    public void onFailure(Call<ResponseBody> call, Throwable t) {
        Log.e(TAG, "error");
    }
});

Also implement this method to file into your sdcard.

private boolean writeToDisk(ResponseBody body) {  
    try { File mediaStorageDir = new File(
                Environment
                        .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
                "ProfileImage");

        // Create the storage directory if it does not exist
        if (!mediaStorageDir.exists()) {
            if (!mediaStorageDir.mkdirs()) {
                Log.e("ProfileImage", "Oops! Failed create "
                        + "ProfileImage" + " directory");
            }
        }
        File futureStudioIconFile = new File(mediaStorageDir.getPath() + File.separator
                + "userImage.png");

        InputStream inputStream = null;
        OutputStream outputStream = null;

        try {
            byte[] fileReader = new byte[4096];

            long fileSize = body.contentLength();
            long fileSizeDownloaded = 0;

            inputStream = body.byteStream();
            outputStream = new FileOutputStream(futureStudioIconFile);

            while (true) {
                int read = inputStream.read(fileReader);

                if (read == -1) {
                    break;
                }

                outputStream.write(fileReader, 0, read);

                fileSizeDownloaded += read;

                Log.d(TAG, "file download: " + fileSizeDownloaded + " of " + fileSize);
            }

            outputStream.flush();

            return true;
        } catch (IOException e) {
            return false;
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }

            if (outputStream != null) {
                outputStream.close();
            }
        }
    } catch (IOException e) {
        return false;
    }
}
Prashant Sharma
  • 1,357
  • 1
  • 21
  • 31