我必须动态生成一个大文件。读取数据库并将其发送给客户端。 我读了一些文档,我做了这个
val streamContent: Enumerator[Array[Byte]] = Enumerator.outputStream {
os =>
// new PrintWriter() read from database and for each record
// do some logic and write
// to outputstream
}
Ok.stream(streamContent.andThen(Enumerator.eof)).withHeaders(
CONTENT_DISPOSITION -> s"attachment; filename=someName.csv"
)
我对scala一般都是新手一般只用了一个星期,因此不能指导我的声誉。
我的问题是:
1)这是最好的方法吗?我发现这个如果我有一个大文件,这将加载到内存中,并且也不知道在这种情况下什么是块大小,如果它将为每个write()
发送不方便。
2)我发现这个方法Enumerator.fromStream(data : InputStream, chunkedSize : int)
更好一点,因为它有一个块大小,但我没有一个inputStream导致即时创建文件。
答案 0 :(得分:4)
docs for Enumerator.outputStream
中有一条注释:
不是 [sic!] 对write的调用不会阻塞,所以如果被输入的iteratee使用输入的速度很慢,则OutputStream不会反推。这意味着它不应该与大流一起使用,因为存在内存不足的风险。
如果发生这种情况取决于您的情况。如果你可以并且将在几秒钟内产生千兆字节,你应该尝试不同的东西。我不确定是什么,但我从Enumerator.generateM()
开始。但是对于很多情况来说,你的方法非常好。看看at this example by Gaëtan Renaudeau for serving a Zip file that's generated on the fly in the same way you're using it:
val enumerator = Enumerator.outputStream { os =>
val zip = new ZipOutputStream(os);
Range(0, 100).map { i =>
zip.putNextEntry(new ZipEntry("test-zip/README-"+i+".txt"))
zip.write("Here are 100000 random numbers:\n".map(_.toByte).toArray)
// Let's do 100 writes of 1'000 numbers
Range(0, 100).map { j =>
zip.write((Range(0, 1000).map(_=>r.nextLong).map(_.toString).mkString("\n")).map(_.toByte).toArray);
}
zip.closeEntry()
}
zip.close()
}
Ok.stream(enumerator >>> Enumerator.eof).withHeaders(
"Content-Type"->"application/zip",
"Content-Disposition"->"attachment; filename=test.zip"
)
请注意,在较新版本的Play中Ok.stream
已被Ok.chunked
取代,以防您想要升级。
对于块大小,您始终可以使用Enumeratee.grouped
来收集一堆值并将它们作为一个块发送。
val grouper = Enumeratee.grouped(
Traversable.take[Array[Double]](100) &>> Iteratee.consume()
)
然后你会做类似
的事情Ok.stream(enumerator &> grouper >>> Enumerator.eof)