2014-05-19 60 views
5

我必須隨時生成大文件。讀取數據庫並將其發送給客戶端。 我讀了一些文件和我這樣做如何隨時生成大數據流

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" 
     ) 

林相當新的階一般只有一個星期,所以不要引導我的名譽。

我的問題是:

1)這是最好的方法嗎?我發現這個如果我有一個大文件,這個會加載到內存中,也不知道這種情況下chunk的大小是什麼,如果它發送給每個write()都不方便。

2)我發現這個方法Enumerator.fromStream(data : InputStream, chunkedSize : int)稍好一點,因爲它有一個塊大小,但我沒有inputStream導致即時創建文件。

+0

塊大小爲默認設置爲'1024 * 8'。我認爲這個大小取決於你的選擇,無論你想發送更大還是更小的塊。 – goral

+0

@goral你怎麼知道'Enumerator.outputStream'塊由'1024 * 8'? – nachokk

+0

我不知道outputStream,你說的'fromStream'方法和規格設置爲默認值 – goral

回答

4

有一個在docs for Enumerator.outputStream一張紙條:[!原文]

調用寫不會阻塞,因此,如果被送入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" 
) 

請記住,Ok.stream已經在玩的較新版本被替換爲Ok.chunked,在要升級的情況。

至於塊大小,您始終可以使用Enumeratee.grouped來收集一堆值並將它們作爲一個塊發送。

val grouper = Enumeratee.grouped( 
    Traversable.take[Array[Double]](100) &>> Iteratee.consume() 
) 

然後你會做這樣的事情

Ok.stream(enumerator &> grouper >>> Enumerator.eof) 
+0

+ 1謝謝你的回答,這是非常有用的,即時通訊非常適合scala對於我來說,閱讀和理解代碼很容易。但是在你提供的鏈接中,*這個演示展示瞭如何即時生成一個zip文件,並直接將它流式傳輸到一個HTTP客戶端**,而無需將其加載到內存中**或將其存儲在一個文件中。說關於千兆字節它是矛盾的outOfMemory – nachokk

+0

是的,我知道,斯卡拉很難。花了我一段時間,我仍然是一個初學者。但是你可以在開始時使用更多的Java/OO代碼,然後[開始試驗函數式編程](https://www.coursera.org/course/progfun)。不要把字面上的「沒有加載到內存中」。你必須在內存中存儲_something_。這僅僅意味着你不會在內存中生成整個zip文件,然後將其發送到客戶端,而是在生成它時進行流式處理。只要客戶端出現並接收到流,它就會從內存中刪除一樣快。 – Carsten

+0

好的,例如'Enumerator.forStream()'讓你可以設置一個chunkSize,在這種情況下,當它要大塊的時候,比如'1024 * 8中的註釋或輸出流的每次寫入?如果是'grouper'中的魔法代碼是有用的:D – nachokk