2014-05-19 10 views
5

Devo generare un grande file al volo. Leggere il database e inviarlo al client. Ho letto alcuni documenti e ho fatto questoCome generare un grande flusso di dati al volo

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

Im piuttosto nuovo a Scala, in generale, solo una settimana in modo da non guidare per la mia reputazione.

Le mie domande sono:

1) È questo il modo migliore? Ho trovato questo se ho un grande file, questo verrà caricato in memoria, e anche non so qual è la dimensione del chunk in questo caso, se invierà per ogni write() non è conveniente.

2) Ho trovato questo metodo Enumerator.fromStream(data : InputStream, chunkedSize : int) un po 'meglio perché ha una dimensione del blocco, ma non ho un inputStream perché sto creando il file al volo.

+0

dimensioni Chunk è di default impostato a '1024 * 8'. Penso che la dimensione dipenda dalla tua scelta se vuoi inviare pezzi più grandi o più piccoli. – goral

+0

@goral come fai a sapere che il blocco 'Enumerator.outputStream' di' 1024 * 8' ?? – nachokk

+0

Non so di outputStream, stavi parlando del metodo 'fromStream' e delle specifiche che sono impostate come valore predefinito – goral

risposta

4

C'è una nota nel docs for Enumerator.outputStream: [sic!]

Non che chiama a scrivere non bloccherà, quindi se l'iteratee che viene alimentato a è lento a consumare l'ingresso, l'OutputStream non respingerò. Ciò significa che non deve essere utilizzato con flussi di grandi dimensioni poiché esiste il rischio di esaurimento della memoria.

Se ciò può accadere dipende dalla situazione. Se riesci a generare Gigabyte in pochi secondi, probabilmente dovresti provare qualcosa di diverso. Non sono esattamente sicuro di cosa, ma inizierei da Enumerator.generateM(). Per molti casi, però, il tuo metodo è perfetto. Dai un'occhiata 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" 
) 

Si prega di tenere presente che Ok.stream è stato sostituito da Ok.chunked nelle versioni più recenti di gioco, nel caso in cui si desidera aggiornare.

Per quanto riguarda la dimensione del blocco, è sempre possibile utilizzare Enumeratee.grouped per raccogliere un gruppo di valori e inviarli come un unico blocco.

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

Poi si farebbe qualcosa di simile

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

+ 1 Grazie per la risposta, è molto utile, molto noob in scala è ancora complesso per me leggere e capire facilmente il codice. Ma nel link che fornisci dice * Questa demo mostra come generare un file zip al volo e lo streaming direttamente su un client HTTP ** senza caricarlo nella memoria ** o memorizzarlo in un file. * E cosa Dite di gigabyte è contraddittorio su outOfMemory – nachokk

+0

Sì, lo so, Scala è difficile. Ci ho messo anche un po 'di tempo, e sono ancora un principiante. Ma puoi usare più codice simile a Java/OO all'inizio e poi [iniziare a sperimentare con la programmazione funzionale] (https://www.coursera.org/course/progfun). Non prendere il _ "senza caricarlo in memoria" _ letteralmente. Devi memorizzare _qualcosa_ in memoria.Significa semplicemente che non si genera l'intero file zip in memoria e quindi lo si invia al client, ma invece lo si trasmette mentre lo si genera. E finché il cliente arriva e prende il flusso, viene eliminato dalla memoria altrettanto velocemente. – Carsten

+0

Ok, ad esempio 'Enumerator.forStream()' ti permette di impostare un chunkSize, in questo caso quando sta andando in chunk, come il commento in '1024 * 8' o in ogni scrittura del flusso di output? Se è che il codice magico in 'grouper' è utile: D – nachokk

Problemi correlati