在代理从S3下载到客户端的简单情况下,我在下载中间处理客户端断开连接时遇到问题。
val enumerator = Enumerator.outputStream{ out =>
val s3Object = s3Client.getObject(new GetObjectRequest(bucket, path))
val in = new BufferedInputStream(s3Object.getObjectContent())
val bufferedOut = new BufferedOutputStream(out)
Iterator.continually(in.read).takeWhile(_ != -1).foreach(bufferedOut.write)
in.close()
bufferedOut.close()
}
Ok.chunked(enumerator.andThen(Enumerator.eof).withHeaders(
"Content-Disposition" -> s"attachment; filename=${name}"
)
只要客户端在完成下载之前没有取消下载,它(大部分)都可以很好地工作。否则,在取消时,枚举器会一直填写数据,直到S3完成下载。几个取消的下载可能会占用相当多的资源。
是否有更好的模式可以防止这种情况发生?
答案 0 :(得分:2)
将InputStream
关闭到onDoneEnumerating
块,并使用Enumerator.fromStream
代替Enumerator.outputStream
:
val s3Object = s3Client.getObject(new GetObjectRequest(bucket, path))
val in = s3Object.getObjectContent()
val enumerator = Enumerator.fromStream(in).onDoneEnumerating {
in.close()
}
fromStream
一次读取块(默认为8 KiB),因此不需要BufferedInputStream
。
此外,对于outputStream
,如果消耗数据的速度很慢,则Iteratee
无法撤回,这可能会导致大量数据(最大为S3对象)在内存中缓冲。这很危险,因为应用程序可能会耗尽内存。使用fromStream
,在Iteratee
准备好接收更多数据之前,不会进行下一次读取。