在客户端取消分块响应时停止执行Enumerator

时间:2014-04-30 17:00:57

标签: scala playframework amazon-s3 stream playframework-2.2

在代理从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完成下载。几个取消的下载可能会占用相当多的资源。

是否有更好的模式可以防止这种情况发生?

1 个答案:

答案 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准备好接收更多数据之前,不会进行下一次读取。