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?
Suraj Vaishnav
En kotlin
Hacer 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:
- 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
-
Aquí estoy usando la palabra clave suspender antes
downloadFile
para llamar a esto desde ViewModel, he usadoviewModelScope.launch {}
puede usar diferentes alcances de rutina de acuerdo con el extremo de la persona que llama. -
Ahora
pathWhereYouWantToSaveFile
si 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
- 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
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")
}
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 @POST
pero depende de si su servidor admite el POST
¡método!
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)
}
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
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