How to download PDF file with Retrofit and Kotlin coroutines?

后端 未结 3 774
长发绾君心
长发绾君心 2020-12-21 02:23

I saw topics like How to download file in Android using Retrofit library?, they use @Streaming and RxJava / callbacks.

I have Kotlin, coroutines, Retrof

3条回答
  •  隐瞒了意图╮
    2020-12-21 02:40

    Thanks to @AndreiTanana I found a mistake. A problem was in suspend in the request definition. All other requests retain their suspend modifier, but this request removed it. I changed the code so.

    interface Api {
        @FormUrlEncoded
        @Streaming
        @POST("export-pdf/")
        fun exportPdf(
            @Field("token") token: String
        ): Call
    
        // Any another request. Note 'suspend' here.
        @FormUrlEncoded
        @POST("reject/")
        suspend fun reject(): RejectResponse
    }
    

    Then in it's implementation, ApiImpl:

    class ApiImpl : Api {
    
        private val retrofit by lazy { ApiClient.getRetrofit().create(Api::class.java) }
    
        override fun exportPdf(
            token: String
        ): Call =
            retrofit.exportPdf(token)
    
        override suspend fun reject(): RejectResponse =
            // Here can be another instance of Retrofit.
            retrofit.reject()
    }
    

    Retrofit client:

    class ApiClient {
    
        companion object {
    
            private val retrofit: Retrofit
    
    
            init {
    
                val okHttpClient = OkHttpClient().newBuilder()
                    .connectTimeout(60, TimeUnit.SECONDS)
                    .readTimeout(60, TimeUnit.SECONDS)
                    .writeTimeout(60, TimeUnit.SECONDS)
                    .build()
    
                val gson = GsonBuilder().setLenient().create()
    
                retrofit = Retrofit.Builder()
                    .baseUrl(SERVER_URL)
                    .client(okHttpClient)
                    // .addConverterFactory(GsonConverterFactory.create(gson)) - you can add this line, I think.
                    .build()
            }
    
            fun getRetrofit(): Retrofit = retrofit
    }
    

    Interactor:

    interface Interactor {
        // Note 'suspend' here. This is for coroutine chain.
        suspend fun exportPdf(
            token: String
        ): Call
    }
    
    class InteractorImpl(private val api: Api) : Interactor {
        override suspend fun exportPdf(
            token: String
        ): Call =
            api.exportPdf(token)
    }
    

    Then in fragment:

    private fun exportPdf(view: View, token: String) {
        showProgress(view)
        launch(Dispatchers.IO) {
            try {
                val response = interactor.exportPdf(token).execute()
                var error: String? = null
                if (response.headers().get("Content-Type")?.contains(
                        "application/json") == true) {
                    // Received JSON with an error.
                    val json: String? = response.body()?.string()
                    error = json?.let {
                        val export = ApiClient.getGson().fromJson(json,
                            ExportPdfResponse::class.java)
                        export.errors?.common?.firstOrNull()
                    } ?: getString(R.string.request_error)
                } else {
                    // Received PDF.
                    val buffer = response.body()?.byteStream()
                    if (buffer != null) {
                        val file = context?.let { createFile(it, "pdf") }
                        if (file != null) {
                            copyStreamToFile(buffer, file)
                            launch(Dispatchers.Main) {
                                if (isAdded) {
                                    hideProgress(view)
                                }
                            }
                        }
                    }
                }
                if (error != null) {
                    launch(Dispatchers.Main) {
                        if (isAdded) {
                            hideProgress(view)
                            showErrorDialog(error)
                        }
                    }
                }
            } catch (e: Exception) {
                launch(Dispatchers.Main) {
                    if (isAdded) {
                        showErrorDialog(getString(R.string.connection_timeout))
                        hideProgress(view)
                    }
                }
            }
        }
    }
    

提交回复
热议问题