¿Cómo descargar un archivo en Android usando la biblioteca Retrofit?

7 minutos de lectura

Avatar de usuario del Dr. Ehsan Ali
Dr. Ehsan Alí

Necesito descargar todo tipo de archivos (binario, imagen, texto, etc.) usando la biblioteca Retrofit en mi aplicación. Todos los ejemplos en la red usan el método HTML GET. Necesito usar POST para evitar el almacenamiento en caché automático.

Mi pregunta es ¿cómo descargar un archivo usando el método POST en Retrofit?

  • prueba esto: stackoverflow.com/questions/32878478/…

    – ugali suave

    17/04/2016 a las 21:01

  • No intentes esto 🙂 Es la segunda respuesta a la pregunta.

    – CoolMind

    10/07/2019 a las 14:30

Avatar de usuario de Suraj Vaishnav
Suraj Vaishnav

En kotlinHacer esto:

En su servicio agregue el método:

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

Para llamar a este método, desde ViewModel:

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

Para guardar el archivo:

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 ""
    }

Nota:

  1. Asegúrese de que su refrofit la URL base del cliente y la URL pasada a downloadFile hacen que la URL del archivo sea válida:

URL base de Retrofit + URL del método downloadFile = URL del archivo

  1. Aquí estoy usando la palabra clave suspender antes downloadFile para llamar a esto desde ViewModel, he usado viewModelScope.launch {} puede usar diferentes alcances de rutina de acuerdo con el extremo de la persona que llama.

  2. Ahora pathWhereYouWantToSaveFilesi desea almacenar un archivo en el directorio de archivos del proyecto, puede hacer esto:

val fileName=url.substring(url.lastIndexOf("/")+1)
val pathWhereYouWantToSaveFile = myApplication.filesDir.absolutePath+fileName
  1. Si está almacenando el archivo descargado en un archivo o directorio de caché, no necesita adquirir permiso, de lo contrario, para el almacenamiento público, conoce el proceso.

  • ” Use JsonReader.setLenient(true) para aceptar JSON mal formado en la línea 1 columna 1 ruta $” -> este es el error que tengo

    – Mostafá Imani

    23 de mayo de 2022 a las 5:52

  • después de agregar GsonBuilder() .setLenient() .create(); y agréguelo a addConverterFactory() si aparece un nuevo error de que el cuerpo de respuesta no tiene argumento

    – Mostafá Imani

    25 de mayo de 2022 a las 4:19

Avatar de usuario de NaviRamyle
NaviRamyle

Usar @Streaming

Asincrónico

EDITAR 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) {

        }
    });

Sincrónico

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

  • Gracias por la respuesta, lo intentaré y te responderé.

    – Dr. Ehsan Ali

    1 de octubre de 2015 a las 2:38

  • Esa es una clase de objeto, puedes cambiarla a solo Object

    – Navi Ramyle

    1 de octubre de 2015 a las 7:17

  • Recibo este error: solo los métodos que tienen Respuesta como tipo de datos pueden tener la anotación @Streaming. Estoy usando el modo asíncrono.

    – Dr. Ehsan Ali

    5 de octubre de 2015 a las 1:43


  • Lo resolví cambiando POJO a la clase de Respuesta. Gracias.

    – Dr. Ehsan Ali

    5 de octubre de 2015 a las 1:56

  • @Ehsan puede usar Response (import retrofit.client.Response;) como modelo y obtener una cadena de ese objeto de respuesta.

    – bheatcoker

    20 de abril de 2016 a las 7:13


Esto es Cómo DESCARGAR un archivo en 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) {
                    }
                });
}

  • Este código ni siquiera compilará. No puede inicializar las cosas en un Interface.

    –Bryan Bryce

    09/08/2017 a las 20:38

  • ¿intentaste compilarlo? funciona para mí, y así es como solía hacerlo

    – ugali suave

    09/08/2017 a las 20:56

  • Para IOUtils, agregue la implementación de dependencia ‘org.apache.directory.studio:org.apache.commons.io:2.4’

    – tendai

    12 de noviembre de 2021 a las 4:22

  • java.lang.RuntimeException: no se pudo invocar public com.squareup.okhttp.ResponseBody() sin argumentos Este es EL ERROR

    – Mostafá Imani

    23 de mayo de 2022 a las 8:08

Con Kotlin, es un poco simple.

Servicio API

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

Cliente API

object ApiClient {
    private val retrofit = ...

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

Función de descarga

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

Avatar de usuario de Spark.Bao
Chispa.Bao

Si usa Retrofit 2.0.0, puede consultar mi respuesta bajo la pregunta: Use retrofit para descargar el archivo de imagen.

El punto clave es el uso okhttp3.ReponseBody para recibir los datos binarios sin procesar, no cualquier POJO.

y quieres usar POST método para obtener el archivo, es fácil, solo cambie @GET a @POSTpero depende de si su servidor admite el POST ¡método!

Avatar de usuario de CoolMind
CoolMind

Puede usar el siguiente código para descargar con progreso (Kotlin)

Servicio de modificación de API

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

asegúrese de agregar @Streaming para descargar archivos grandes

Y pegue el código debajo en su Actividad o Fragmento

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

a continuación se muestra el método writeResponseBodyToDisk

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
    }

}

El siguiente método es para calcular el progreso.

 fun calulateProgress(totalSize:Double,downloadSize:Double):Double{
    return ((downloadSize/totalSize)*100)
}

Avatar de usuario de Ameen Maheen
ameen maheen

Utilicé el siguiente código para descargar cualquier tipo de archivo mediante actualización…

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

  • funciona en mi android 5.1 pero no en otros. Lo he probado en android 6 y no funciona 0

    – Rodrigo

    8 de agosto de 2017 a las 15:53

¿Ha sido útil esta solución?