You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Okio 3.x 下使用扩展函数将Retrofit响应体流式写入文件的正确实现方案(含资源安全关闭)

Okio 3.x 下使用扩展函数将Retrofit响应体流式写入文件的正确实现方案(含资源安全关闭)

嘿,刚好我最近也完成了Okio 3.x的迁移,碰到过一模一样的问题!这就给你拆解错误原因,再给出符合Okio 3.x idiom的正确写法,还能保证资源安全关闭~

先说说你遇到的错误原因

  1. 静态方法变扩展函数:Okio 3.x把sink(File)和buffer(Sink)这类核心方法从静态工具类调用改成了扩展函数,所以原来Okio.sink(destination)、Okio.buffer(sink)的写法就失效了,得换成对象调用的形式。
  2. 空安全与类型匹配:response.body()?.source()返回的是BufferedSource?,虽然BufferedSource是Source的子类,但空值需要处理,直接传给writeAll会触发类型不匹配的报错。

最小可行实现(同步调用)

下面是完整的同步版本代码,用Kotlin的use函数自动管理资源,无需手动调用close(),绝对安全:

import okhttp3.ResponseBody
import okio.buffer
import okio.sink
import java.io.File

// 假设你的Retrofit Api接口定义如下
interface Api {
    @GET
    fun downloadFileFromUrl(@Url url: String): Call<ResponseBody>
}

fun downloadFile(api: Api, url: String, destination: File) {
    val response = api.downloadFileFromUrl(url).execute()

    // 先确保请求成功且响应体非空,抛出明确的错误信息
    val responseBody = requireNotNull(response.body()) {
        "下载失败:响应体为空"
    }
    check(response.isSuccessful) {
        "下载失败:HTTP状态码 ${response.code}"
    }

    // 用use函数自动关闭资源,异常时也会正确释放
    destination.sink().buffer().use { bufferedSink ->
        responseBody.source().use { bufferedSource ->
            bufferedSink.writeAll(bufferedSource)
        }
    }
}

关键要点拆解

  • 扩展函数的正确用法:
    • 原来的Okio.sink(destination) → 现在直接用destination.sink()(File的扩展函数)
    • 原来的Okio.buffer(sink) → 现在用sink.buffer()(Sink的扩展函数),还可以链式写成destination.sink().buffer()
  • 资源安全关闭:
    Kotlin的use函数是处理Closeable资源的最佳实践,它会在代码块执行完毕(不管正常结束还是抛出异常)自动调用close(),彻底避免资源泄漏,比手动写try-finally简洁太多。
  • 空安全与响应校验:
    用requireNotNull确保响应体非空,用check验证HTTP状态码是2xx,让错误信息更清晰,排查问题更方便。

协程版本(推荐,适配Retrofit协程支持)

如果你的项目用了Kotlin协程,Retrofit也支持suspend函数,写法会更简洁:

import okhttp3.ResponseBody
import okio.buffer
import okio.sink
import java.io.File

interface Api {
    @GET
    suspend fun downloadFileFromUrl(@Url url: String): ResponseBody
}

suspend fun downloadFileCoroutine(api: Api, url: String, destination: File) {
    // 协程调用下,Retrofit会直接返回ResponseBody(请求失败会抛出异常)
    val responseBody = api.downloadFileFromUrl(url)

    // 同样用use函数管理资源
    destination.sink().buffer().use { bufferedSink ->
        responseBody.source().use { bufferedSource ->
            bufferedSink.writeAll(bufferedSource)
        }
    }
}

协程版本里,Retrofit会自动处理请求的异常(比如HTTP错误会抛出HttpException),不需要手动校验isSuccessful,代码更清爽。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 07:49:30