2012-11-09 13 views
5

Qualcuno può dirmi come clonare un inputstream, prendendo il minor tempo possibile per la creazione? Ho bisogno di clonare un inputstream più volte per più metodi per elaborare l'IS. Ho provato tre modi e le cose non funzionano per un motivo o per l'altro.Come clonare un inputstream in java in un tempo minimo

Metodo n. 1: Grazie alla community di stackoverflow, ho trovato utile il seguente link e ho incorporato lo snippet di codice nel mio programma.

How to clone an InputStream?

Tuttavia, utilizzando questo codice può richiedere fino a un minuto (per un file di 10 MB) per creare le inputstreams clonati e il mio programma ha bisogno di essere il più veloce possibile.

int read = 0; 
    byte[] bytes = new byte[1024*1024*2]; 

    ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
    while ((read = is.read(bytes)) != -1) 
     bos.write(bytes,0,read); 
    byte[] ba = bos.toByteArray(); 

    InputStream is1 = new ByteArrayInputStream(ba); 
    InputStream is2 = new ByteArrayInputStream(ba); 
    InputStream is3 = new ByteArrayInputStream(ba); 

Metodo # 2: Ho anche provato ad utilizzare BufferedInputStream per clonare la IS. Questo è stato veloce (tempo di creazione più lento == 1 ms. Più veloce == 0 ms). Tuttavia, dopo che ho inviato is1 per essere elaborato, l'elaborazione dei metodi è2 e is3 ha generato un errore che diceva che non c'era nulla da elaborare, quasi come tutte le 3 variabili sotto indicate con lo stesso IS.

is = getFileFromBucket(path,filename); 
    ... 
    ... 
    InputStream is1 = new BufferedInputStream(is); 
    InputStream is2 = new BufferedInputStream(is); 
    InputStream is3 = new BufferedInputStream(is); 

Metodo # 3: penso che il compilatore sta mentendo a me. Ho controllato markSupported() per is1 per i due esempi sopra. E 'tornato vero così ho pensato che avrei potuto correre

is1.mark() 
    is1.reset() 

o semplicemente

is1.reset(); 

prima di passare la è di mio rispettivi metodi. In entrambi gli esempi precedenti, viene visualizzato un errore che indica che si tratta di un segno non valido.

Sono fuori di idee ora, quindi grazie in anticipo per qualsiasi aiuto tu possa darmi.

P.S. Dai commenti che ho ricevuto dalle persone, ho bisogno di chiarire un paio di cose riguardanti la mia situazione: 1) Questo programma è in esecuzione su una VM 2) L'inputstream viene passato in me da un altro metodo. Io non sto leggendo da un file locale 3) La dimensione del InputStream non è noto

+3

L'esecuzione del codice per il metodo n. 1 richiede 18 ms (per un file da 10 MB) sul computer. C'è qualcosa che non va nel tuo hardware? –

+0

Grazie per la risposta. Non penso che ci sia qualcosa di sbagliato nel mio hardware. Mi ha appena colpito che ho dimenticato di menzionare 2 cose: a) questo è su una VM eb) l'inputstream è di un file jpg. Il tempo più veloce è di 11 secondi, ma osservo i miei test, la media è di circa 30 secondi, il più lento è di circa 1 minuto (53 secondi per l'esattezza). – Classified

+1

Se si esegue questa operazione, è possibile ottenere un incremento minore: byte [] ba = new byte [is.available()]; // Funziona se si tratta di un FileInputStream nuovo DataInputStream (is) .readFully (ba); –

risposta

3

Tuttavia, utilizzando questo codice può richiedere fino a un minuto (per un file di 10 MB) per creare le inputstreams clonati e la mia il programma deve essere il più veloce possibile.

Ben copiare un flusso richiede tempo e (in generale) è l'unico modo per clonare un flusso. A meno che non si rafforzi la portata del problema, ci sono poche possibilità che le prestazioni possano essere significativamente migliorate.

Qui ci sono un paio di circostanze in cui il miglioramento è possibile:

  • Se sapessi in anticipo il numero di byte nel flusso allora si può leggere direttamente l'array di byte finale.

  • Se si sapeva che i dati provengono da un file, è possibile creare un buffer mappato in memoria per il file.

Ma il problema fondamentale è che lo spostamento di molti byte richiede tempo. E il fatto che ci vogliano 1 minuto per un file da 10 Mb (usando il codice nella domanda) suggerisce che indica che il vero collo di bottiglia non è affatto in Java.

1

Avete intenzione di eseguire i metodi separati in parallelo o in sequenza? Se sequenzialmente, non vedo alcun motivo per clonare il flusso di input, quindi devo presumere che stiate progettando di far girare i thread per gestire ogni flusso.

Non sono vicino a un computer in questo momento per testarlo, ma sto pensando che sarebbe meglio leggere l'input in blocchi, ad esempio 1024 byte, e quindi spingere quei blocchi (o copie di array del pezzi) sui flussi di output con flussi di input collegati alle estremità dei fili. Avere i tuoi lettori bloccano se non ci sono dati disponibili, ecc

+0

Grazie per la tua risposta e il tuo suggerimento. Sì, ho intenzione di usare i fili ... una volta capito come aggiustare questo collo della bottiglia. Proverò a farlo, leggere in blocchi, ma sto ricevendo il flusso di input passato da un altro metodo, quindi ho bisogno di vedere se è fattibile nel mio caso. – Classified

2

quanto riguarda il tuo primo approccio, quello che consiste nel mettere tutti i tuoi bytes in un ByteArrayOutputStream:

  • In primo luogo, questo approccio consuma un sacco di memoria. Se non si assicura che JVM si avvii con sufficiente memoria allocata, sarà necessario richiedere dinamicamente la memoria durante l'elaborazione del flusso e questo richiede molto tempo.
  • Your ByteArrayOutputStream viene inizialmente creato con un buffer di 32 byte. Ogni volta che provate a inserirvi qualcosa, se non si adatta all'array di byte esistente viene creato un nuovo array più grande e i vecchi byte vengono copiati in quello nuovo. Poiché stai utilizzando ogni volta un ingresso da 2MB, stai forzando ByteArrayOutputStream a copiare i suoi dati più e più volte in array più grandi, aumentando la dimensione del suo array in 2MB ogni volta.
  • Poiché i vecchi array sono spazzatura, è probabile che la loro memoria venga recuperata dal garbage collector, il che rende ancora più lento il processo di copia.
  • Forse dovresti definire ByArrayOutputStream usando il costruttore che specifica una dimensione del buffer iniziale. Quanto più accuratamente si imposta la dimensione, tanto più veloce dovrebbe essere il processo perché saranno necessarie meno copie intermedie.

Il secondo approccio è fasullo, non è possibile decorare lo stesso flusso di input in altri flussi e aspettarsi che le cose funzionino. Poiché i byte vengono consumati da un flusso, anche il flusso interno viene esaurito e non è possibile fornire agli altri flussi dati accurati.

Prima di estendere la risposta, chiedimi, gli altri tuoi metodi prevedono di ricevere copie del flusso di input in esecuzione su un thread separato? Perché se è così, questo suona come il lavoro per PipedOutputStream e PipedInputStream?

+0

Grazie per la risposta. Dato che un altro metodo sta passando l'inputstream a me non conosco la dimensione dell'IS in arrivo. Ho giocato con il fatto che l'array di byte fosse 8MB ma ci volle ancora molto tempo. Qualcuno mi ha suggerito di usare BufferedInputStream e credo che non stavo usando correttamente quindi il mio male per l'uso fasullo =) Ho intenzione di usare i thread per i miei altri metodi quindi cercherò di vedere il tuo suggerimento su PipedIS e PipedOS per vedere se aiuta In questo momento, sto solo cercando di far funzionare tutto in serie prima di iniziare a giocare con i thread. – Classified

6

come clonare un inputstream, prendendo il minimo tempo di creazione possibile? Ho bisogno di clonare un InputStream più volte per diversi metodi per elaborare la IS

Si potrebbe semplicemente creare una sorta di una classe personalizzata ReusableInputStream in cui si immediatamente scrive anche ad un interna ByteArrayOutputStream al 1 ° lettura completa, quindi avvolgerlo in un ByteBuffer quando viene letto l'ultimo byte e riutilizzare infine lo stesso ByteBuffer nelle letture successive successive che vengono automaticamente capovolte quando viene raggiunto il limite. Questo ti risparmia da una lettura completa come nel tuo primo tentativo.

Ecco un esempio di kickoff di base:

public class ReusableInputStream extends InputStream { 

    private InputStream input; 
    private ByteArrayOutputStream output; 
    private ByteBuffer buffer; 

    public ReusableInputStream(InputStream input) throws IOException { 
     this.input = input; 
     this.output = new ByteArrayOutputStream(input.available()); // Note: it's resizable anyway. 
    } 

    @Override 
    public int read() throws IOException { 
     byte[] b = new byte[1]; 
     read(b, 0, 1); 
     return b[0]; 
    } 

    @Override 
    public int read(byte[] bytes) throws IOException { 
     return read(bytes, 0, bytes.length); 
    } 

    @Override 
    public int read(byte[] bytes, int offset, int length) throws IOException { 
     if (buffer == null) { 
      int read = input.read(bytes, offset, length); 

      if (read <= 0) { 
       input.close(); 
       input = null; 
       buffer = ByteBuffer.wrap(output.toByteArray()); 
       output = null; 
       return -1; 
      } else { 
       output.write(bytes, offset, read); 
       return read; 
      } 
     } else { 
      int read = Math.min(length, buffer.remaining()); 

      if (read <= 0) { 
       buffer.flip(); 
       return -1; 
      } else { 
       buffer.get(bytes, offset, read); 
       return read; 
      } 
     } 

    } 

    // You might want to @Override flush(), close(), etc to delegate to input. 
} 

(notare che il lavoro effettivo viene eseguito in int read(byte[], int, int) invece che in int read(), e quindi ci si aspetta di essere più veloce quando il chiamante stesso è anche in streaming utilizzando un buffer byte[])

si potrebbe utilizzare come segue:

InputStream input = new ReusableInputStream(getFileFromBucket(path,filename)); 
IOUtils.copy(input, new FileOutputStream("/copy1.ext")); 
IOUtils.copy(input, new FileOutputStream("/copy2.ext")); 
IOUtils.copy(input, new FileOutputStream("/copy3.ext")); 

Per quanto riguarda le prestazioni, 1 minuto per 10 MB è più probabile un problema hardware, non un problema software. Il mio hard disk portatile da 7200rpm lo fa in meno di 1 secondo.

+0

Grazie per lo snippet di codice. Lo proverò insieme agli altri suggerimenti! – Classified

Problemi correlati