我目前正在尝试使用Play框架2.7构建并发送zip存档。我想即时生成档案以避免使用存储(或填满RAM)。
我发现了使用 Enumerator 的一些不错的解决方案,但是使用当前的Play框架版本,我需要构建一个Akka Source
。
我当前的尝试是:
val streamContent = StreamConverters
.asOutputStream()
.mapMaterializedValue( ZipUtil.zip(ds.files) )
Ok.chunked(streamContent)
.as("application/zip")
.withHeaders( CONTENT_DISPOSITION -> "attachment;" )
其中ZipUtil.zip(...)
定义为:
def zip(files: Iterable[Path]): OutputStream => Unit = { os =>
val zipOut = new ZipOutputStream(os)
val bytesBuffer = ByteBuffer.allocate(8192)
files.foreach { f =>
val fileName = f.getFileName.toString
val ze = new ZipEntry(fileName)
zipOut.putNextEntry(ze)
val channel = Files.newByteChannel(f,StandardOpenOption.READ)
bytesBuffer.clear()
var numBytes = channel.read(bytesBuffer)
while (numBytes != -1) {
val bytes = bytesBuffer.array()
zipOut.write(bytes,numBytes)
bytesBuffer.clear()
numBytes = channel.read(bytesBuffer)
}
channel.close()
}
zipOut.close()
}
但是它似乎陷入僵局。如果添加大量日志记录,似乎会陷入putNextEntry
调用中。当然,我在akka流机制之外测试了zip(...)
方法,并且效果很好。
我还尝试使用StreamConverters.fromInputStream
并使用管道IO流将ZipOutputStream
连接到Source
的{{1}},但没有成功。