繁体   English   中英

如何在Play 2.1中使用带有分块响应的OutputStreams

[英]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.Gzipplay.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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM