file - Ktor - 处理大文件操作而不会发生内存泄漏
问题描述
我对后端开发很陌生。基本上,我想创建一个健壮且简单的应用程序,它将接受参数中的 zip 文件 URL,然后从 URL 下载 zip 文件,最后提取 zip 并返回其中的bin
文件。注意:zip 文件的大小范围为 5MB 到 150MB。我已尝试按以下方式进行描述的操作。
package la.sample
import io.ktor.application.Application
import io.ktor.application.call
import io.ktor.client.HttpClient
import io.ktor.client.request.get
import io.ktor.http.HttpStatusCode
import io.ktor.response.respond
import io.ktor.response.respondFile
import io.ktor.routing.get
import io.ktor.routing.routing
import java.io.*
fun Application.startServer() {
routing {
get("/get-bin") {
//Gets the AWS Url from params
val awsUrl = call.request.queryParameters.get("url") ?: "Error"
// Download the zip file from the AWS URL
val client = HttpClient()
val bytes = client.get<ByteArray>(awsUrl)
//Create a temp file on the server & write the zip file bytes into it.
val file = File(".", "data.zip")
file.writeBytes(bytes)
//Call a method to unzip the file
unzipAndReturnBinFile()?.let {
call.respondFile(it) //respond with bin file
} ?: kotlin.run{
call.respond(HttpStatusCode.InternalServerError)
}
}
}
}
fun unzipAndReturnBinFile(): File? {
var exitVal = 0
//Command shell to unzip the file
Runtime.getRuntime().exec("unzip bundle.zip -d data").let {//command shell to unzip the zip file
exitVal += it.waitFor()
}
//Check if the command executed successfully
if (exitVal == 0) {
var binFile: File? = null
//check if the extracted files contain `bin`
File("data").listFiles().forEach {
if (it.name.contains(".bin")) {
binFile = it
}
}
//return bin or null otherwise
return binFile
} else {
throw Exception("Command Shell Execution failed.")
}
}
上面的代码在本地机器上工作正常,不管 Zip 文件的大小。但是当它部署到 AWS 时,如果 zip 或 bin 文件大于 100 MB,则代码会中断并给出java.lang.OutOfMemoryError
错误。如果有人可以向我建议一种在后端处理大型文件操作的正确方法,并且能够处理 100 次这样的并发调用,我将非常感激。谢谢你。
解决方案
根据@Naor 的评论,我已经更新了代码以接受多部分文件,并在获得每个小夹头(部分)后立即将它们写入另一个文件,而无需将整个数据存储在内存中。它解决了这个问题。以下是更新的代码片段。
val file = File(".", Constant.FILE_PATH)
call.receiveMultipart().apply {
forEachPart {
if (it is PartData.FileItem) {
it.streamProvider().use { input ->
file.outputStream().buffered().use { output -> input.copyToSuspend(output) }
}
}
it.dispose
} }
推荐阅读
- java - 将 PHP 加密代码转换为 Java 显示错误?
- spring-boot - spring boot 拦截器检查请求参数中是否存在任何不安全字符并处理它们
- python - 运行多个 websocket 连接
- kentico - 如何在母版页模板中获取当前年份
- python - pd.DataFrame(数据,列=[])。如何传递带有嵌套字典的数据?
- font-awesome - Contao - 如何使用字体图标(例如 Font-Awesome)
- wordpress - 从管理员端上传 webp 图片 - Wordpress
- python - div 使用 BeautifulSoup 抓取价格时返回空,其他所有内容(如标题、图片链接)返回一个值。为什么会发生这种情况?
- powershell - 在第一次失败/成功操作后运行 aspnet_regiis 以进行循环加密
- three.js - 用three.js创建逼真功能区的最佳方法是什么