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

使用Ktor Client下载大文件时遭遇OutOfMemoryError求助

解决Ktor Client下载大文件OutOfMemoryError问题

问题原因

你的代码虽使用了bodyAsChannel(),但Ktor默认启用的**响应保存(SavedCall)**机制会将整个响应体读取到内存缓存,150MB的文件直接占满内存触发OOM,错误栈里的SavedCallKt.save也佐证了这一点。

修复方案

1. 禁用响应保存机制

在请求或全局配置中关闭响应保存,避免文件全量加载到内存:

val client = HttpClient(CIO) {
    install(HttpTimeout) {
        val timeout = 30_000L
        requestTimeoutMillis = timeout
        connectTimeoutMillis = timeout
    }
}

val response = client.get("https://example.com/large.PDF") {
    header("Authorization", "Bearer $token")
    timeout {
        // 修正超时单位:原数值是字节,改成毫秒(示例为20分钟)
        connectTimeoutMillis = 20 * 60 * 1000
        requestTimeoutMillis = 20 * 60 * 1000
        socketTimeoutMillis = 20 * 60 * 1000
    }
    // 关键:禁用当前请求的响应缓存
    setOption(HttpCallOptions.SaveCallToHistory, false)
}.bodyAsChannel()

2. 流式写入本地文件

拿到ByteReadChannel后,直接流式写入文件,仅保留小缓冲区数据,避免内存堆积:

// 在IO协程中执行(如viewModelScope、CoroutineScope(Dispatchers.IO))
CoroutineScope(Dispatchers.IO).launch {
    val targetFile = File(getExternalFilesDir(null), "large.pdf")
    targetFile.outputStream().use { output ->
        // 8KB缓冲区,可根据设备性能调整
        response.copyTo(output, bufferSize = 8 * 1024)
    }
    response.cancel() // 关闭通道释放资源
}

额外优化提示

  • 始终在Dispatchers.IO中处理文件读写,避免阻塞主线程
  • 可通过copyTo的onProgress回调实现下载进度监听(Ktor 2.x支持)
  • Android平台优先使用应用私有目录存储文件,无需额外权限

内容的提问来源于stack exchange,提问作者Jemo Mgebrishvili

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 13:20:28