使用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
相关产品推荐
相关产品推荐

