如何使用Retrofit库在Android下载文件?

Ehs*_*san 18 android download retrofit

我需要在我的应用程序中使用Retrofit库下载所有类型的文件(二进制文件,图像,文本等).网上的所有示例都使用HTML GET方法.我需要使用POST来防止自动缓存.

我的问题是如何在Retrofit中使用POST方法下载文件?

Sur*_*nav 14

kotlin,这样做:

在您的服务添加方法中:

    @Streaming
    @GET
    suspend fun downloadFile(@Url fileUrl:String): Response<ResponseBody>
Run Code Online (Sandbox Code Playgroud)

要从 ViewModel 调用此方法:

viewModelScope.launch {
     val responseBody=yourServiceInstance.downloadFile(url).body()
     saveFile(responseBody,pathWhereYouWantToSaveFile)
}
Run Code Online (Sandbox Code Playgroud)

保存文件:

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 ""
    }
Run Code Online (Sandbox Code Playgroud)

笔记:

  1. 确保您的refrofit客户端的基本 url 和传递给 downloadFile 的 url 使文件 url 有效:

Retrofit 的 Base url + downloadFile 的方法 url = File url

  1. 这里我之前使用 suspend 关键字downloadFile从 ViewModel 调用它,我已经使用过viewModelScope.launch {}你可以根据你的调用端使用不同的协程范围。

  2. 现在pathWhereYouWantToSaveFile,如果要将文件存储到项目的文件目录中,可以执行以下操作:

val fileName=url.substring(url.lastIndexOf("/")+1)
val pathWhereYouWantToSaveFile = myApplication.filesDir.absolutePath+fileName
Run Code Online (Sandbox Code Playgroud)
  1. 如果您将下载的文件存储在文件或缓存目录下,则无需获取权限,否则对于公共存储,您知道流程。


Nav*_*yle 13

使用@Streaming

异步

编辑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) {

        }
    });
Run Code Online (Sandbox Code Playgroud)

同步

//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();
}
Run Code Online (Sandbox Code Playgroud)


khc*_*tro 10

对于 Kotlin,这有点简单。

接口服务

@GET
@Streaming
fun download(@Url url: String): Call<ResponseBody>
Run Code Online (Sandbox Code Playgroud)

API客户端

object ApiClient {
    private val retrofit = ...

    val service: ApiService = retrofit.create(ApiService::class.java)
}
Run Code Online (Sandbox Code Playgroud)

下载功能

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")
}
Run Code Online (Sandbox Code Playgroud)


小智 6

您可以使用以下代码进行进度下载(Kotlin)

改装API服务

@Streaming
@GET
fun downloadFile(@Url fileUrl: String): Observable<Response<ResponseBody>>
Run Code Online (Sandbox Code Playgroud)

确保您添加 @Streaming用于大文件下载

并在活动或片段中粘贴以下代码

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)
        })
}
Run Code Online (Sandbox Code Playgroud)

下面是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
    }

}
Run Code Online (Sandbox Code Playgroud)

下面的方法是用于计算进度

 fun calulateProgress(totalSize:Double,downloadSize:Double):Double{
    return ((downloadSize/totalSize)*100)
}
Run Code Online (Sandbox Code Playgroud)

  • @CarsonH​​olzheimer 或更好,当你可以使用 Kotlin 协程时,为什么要使用 RxJava 甚至 AsyncTask?:D (5认同)
  • 拥有RxJava时,为什么还要使用AsyncTask? (4认同)
  • 如果您阅读本文,请不要使用上面的答案。它混合了不同的编程方法,没有任何优势。这使得您的应用程序容易出现错误、错误、可读性缺失等...... (2认同)

uga*_*oft 5

这是如何在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) {
                    }
                });
}
Run Code Online (Sandbox Code Playgroud)

  • 您尝试编译吗?它为我工作,这就是我以前的方式 (3认同)
  • 这段代码甚至无法编译。您无法在“Interface”中初始化事物。 (2认同)