我需要以文件形式提供来自Action的一些内容:基本上,我正在动态创建CSV内容并将其发送给客户端。
我无法使用sendFile,因为该文件确实不存在;我尝试使用chunked transfer,但是响应非常慢(在localhost中我得到的文件大约是100KB / s,我觉得这很奇怪)。
我有没有办法设置内容类型并逐行编写响应"而不必指定内容长度"先验"?
答案 0 :(得分:2)
这是使用简单预定义Enumerator
的一种方法,它将从写入OutputStream
的字节产生响应:
def csv = Action {
val enumerator = Enumerator.outputStream { out =>
out.write(...)
// Keep writing to the Enumerator
out.close()
}
Ok.chunked(enumerator.andThen(Enumerator.eof)).withHeaders(
"Content-Type" -> "text/csv",
"Content-Disposition" -> s"attachment; filename=test.csv"
)
}
对于相对较小的文件(或者如果生成文件的过程本质上很慢),这很简单,但请注意,从文档中这没有背压,将大文件读入OutputStream
如果客户端无法快速下载,可以快速填满内存。
<强>更新强>:
经过多次测试后,您写入Byte
的{{1}}数组的大小似乎会对吞吐量产生巨大影响。
使用此样本流:
OutputStream
以1KB的方式写入val s = Stream.continually(0.toByte)
这样的块会产生6MB / s的吞吐量:
OutputStream
但是,如果我一次只写10个字节,吞吐量会减慢到小于100KB / s。因此,我建议使用此方法以分块形式写入CSV,一次将多行写入(0 until 1024*1024).foreach{i =>
out.write(s.take(1024).toArray)
}
而不是一次写入一行。