4

我需要使用java.util.zip.ZipOutputStream压缩文件存档来响应。

未压缩的数据有几百兆字节,所以我想尽可能少地存储它。它来自 SQL 结果的序列化。

我看到使用 anOutputStream返回分块结果的示例Enumerator.outputStream

但是当我阅读文档时,这些似乎是不明智的(强调我的)

使用 OutputStream 创建一个字节枚举器。

并不是说 write 调用不会阻塞,因此如果被馈送到的迭代器消耗输入的速度很慢,则 OutputStream 不会推回。这意味着它不应该与大型流一起使用,因为存在内存不足的风险

显然,我不能使用它。或者至少不是没有修改。


如何使用OutputStream(在本例中为 gzip 压缩存档)创建响应,同时确保只有部分响应存储在内存中?

InputStream我认识到s/ OutputStreams 和 Play 的Enumerator/范式之间的区别Iteratee,所以我希望会有一种特定的方式来生成我的源数据(SQL 结果的序列化),这样它就不会超过下载速度。我不知道那是什么。

4

2 回答 2

4

一般来说,你不能安全地使用OutputStreamEnumerator/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
  }
}

如果您只需要压缩,请查看中Enumeratee提供的实例play.filters.gzip.Gzipplay.filters.gzip.GzipFilter过滤器。

于 2014-07-13T02:35:33.977 回答
0

唯一的背压机制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)
}

由于操作是阻塞的,因此您必须注意防止死锁。

要么使用两个不同的线程池,要么使用缓存(无界)线程池。

于 2016-07-09T13:39:55.200 回答