[英]How to use OutputStreams with chunked response in Play 2.1
我需要使用java.util.zip.ZipOutputStream
来响应压缩文件存档。
数据是几百兆字节未压缩,所以我想尽可能少地存储。 它来自SQL结果的序列化。
我看到使用OutputStream
使用Enumerator.outputStream
返回分块结果的示例:
但是当我阅读文档时,这些似乎是不明智的(强调我的)
使用OutputStream创建一个字节的枚举器。
并非写入的调用不会阻塞,因此如果被输入的iteratee使用输入的速度很慢,则OutputStream将不会回退。 这意味着它不应该与大流一起使用,因为存在内存不足的风险 。
显然,我无法使用它。 或者至少没有修改。
如何确保使用OutputStream
(在这种情况下是一个gzip压缩文件)创建响应,同时确保只有部分内容存储在内存中?
我认识到InputStream
s / OutputStream
和Play的Enumerator
/ Iteratee
范例之间的区别,所以我希望我需要一种特定的方式来生成我的源数据(SQL结果的序列化),这样它就不会超过速率下载 我不知道它是什么。
通常,您无法安全地将任何OutputStream
与Enumerator / Iteratee框架一起使用,因为OutputStream
不支持非阻塞回送。 但是,如果您可以控制对OutputStream
的写入,您可以将以下内容组合在一起:
val baos = new ByteArrayOutputStream
val zos = new ZipOutputStream(baos)
val enumerator = Enumerator.generateM {
Future.successful {
if (moreDateToWrite) {
// Write data into zos
val r = Some(baos.toByteArray)
baos.reset()
r
} else None
}
}
如果您只需要压缩,请查看play.filters.gzip.Gzip
和play.filters.gzip.GzipFilter
过滤器中提供的Enumeratee
实例。
OutputStream
唯一的反压机制是阻塞线程。 所以,无论如何,都必须有一个能够被阻止的线程。
一种方法是使用管道流。
import java.io.OutputStream
import java.io.PipedInputStream
import java.io.PipedOutputStream
import play.api.libs.iteratee.Enumerator
import scala.concurrent.ExecutorContext
def outputStream2(a: OutputStream => Unit, bufferSize: Int)
(implicit ec1: ExecutionContext, ec2: ExecutionContext) = {
val outputStream = new PipedOutputStream
Future(a(outputStream))(ec1)
val inputStream = new PipedInputStream(pipedOutputStream, bufferSize)
Enumerator.fromStream(inputStream)(ec2)
}
由于操作是阻塞的,因此必须注意防止死锁。
使用两个不同的线程池,或使用缓存(无界)线程池。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.