Un modo semplice per scrivere contenuti di un Java InputStream in un OutputStream

Sono stato sorpreso di scoprire oggi che non riuscivo a rintracciare un modo semplice per scrivere il contenuto di un InputStream su un OutputStream in Java. Ovviamente, il byte buffer code non è difficile da scrivere, ma sospetto che mi manchi solo qualcosa che mi renderebbe la vita più facile (e il codice più chiaro).

Quindi, dato un InputStream in e un OutputStream out , c’è un modo più semplice per scrivere quanto segue?

 byte[] buffer = new byte[1024]; int len = in.read(buffer); while (len != -1) { out.write(buffer, 0, len); len = in.read(buffer); } 

Se si utilizza Java 7, i file (nella libreria standard) rappresentano l’approccio migliore:

 /* You can get Path from file also: file.toPath() */ Files.copy(InputStream in, Path target) Files.copy(Path source, OutputStream out) 

Modifica: Ovviamente è utile quando crei uno di InputStream o OutputStream dal file. Utilizzare file.toPath() per ottenere il percorso dal file.

Per scrivere in un file esistente (ad esempio, uno creato con File.createTempFile() ), devi passare l’opzione di copia REPLACE_EXISTING (altrimenti viene generata l’ FileAlreadyExistsException ):

 Files.copy(in, target, StandardCopyOption.REPLACE_EXISTING) 

Come menzionato da WMR, org.apache.commons.io.IOUtils di Apache ha un metodo chiamato copy(InputStream,OutputStream) che fa esattamente quello che stai cercando.

Quindi, hai:

 InputStream in; OutputStream out; IOUtils.copy(in,out); in.close(); out.close(); 

… nel tuo codice.

C’è una ragione per cui stai evitando IOUtils ?

Penso che funzionerà, ma assicurati di testarlo … un piccolo “miglioramento”, ma potrebbe essere un po ‘un costo per la leggibilità.

 byte[] buffer = new byte[1024]; int len; while ((len = in.read(buffer)) != -1) { out.write(buffer, 0, len); } 

Java 9

Da Java 9, InputStream fornisce un metodo chiamato transferTo con la seguente firma:

 public long transferTo(OutputStream out) throws IOException 

Come afferma la documentazione , transferTo :

Legge tutti i byte da questo stream di input e scrive i byte nel stream di output specificato nell’ordine in cui vengono letti. Al ritorno, questo stream di input sarà alla fine del stream. Questo metodo non chiude nessuno stream.

Questo metodo può bloccare la lettura indefinitamente dal stream di input o la scrittura nel stream di output. Il comportamento per il caso in cui il stream di input e / o di output è chiuso in modo asincrono o il thread interrotto durante il trasferimento è altamente specifico per input e output, e quindi non specificato

Quindi, per scrivere contenuti di un Java InputStream su un OutputStream , puoi scrivere:

 input.transferTo(output); 

Utilizzo di ByteStreams.copy() :

 ByteStreams.copy(inputStream, outputStream); 

Funzione semplice

Se ti serve solo per scrivere un InputStream su un File , puoi utilizzare questa semplice funzione:

 private void copyInputStreamToFile( InputStream in, File file ) { try { OutputStream out = new FileOutputStream(file); byte[] buf = new byte[1024]; int len; while((len=in.read(buf))>0){ out.write(buf,0,len); } out.close(); in.close(); } catch (Exception e) { e.printStackTrace(); } } 

PipedInputStream e PipedOutputStream devono essere utilizzati solo quando si dispone di più thread, come indicato da Javadoc .

Inoltre, tieni presente che i flussi di input e i flussi di output non avvolgono interruzioni di thread con IOException s … Pertanto, dovresti prendere in considerazione l’incorporazione di un criterio di interruzione nel codice:

 byte[] buffer = new byte[1024]; int len = in.read(buffer); while (len != -1) { out.write(buffer, 0, len); len = in.read(buffer); if (Thread.interrupted()) { throw new InterruptedException(); } } 

Questa sarebbe un’utile aggiunta se si prevede di utilizzare questa API per copiare grandi volumi di dati o dati da flussi che rimangono bloccati per un tempo intollerabilmente lungo.

Non c’è modo di farlo molto più facilmente con i metodi JDK, ma come ha già notato Apocalisp, non sei l’unico con questa idea: potresti usare IOUtils da Jakarta Commons IO , ma ha anche molte altre cose utili, che IMO dovrebbe essere parte del JDK …

Il JDK usa lo stesso codice, quindi sembra che non ci sia un modo “più semplice” senza le librerie di terze parti (che probabilmente non fanno nulla di diverso in ogni caso). Quanto segue è copiato direttamente da java.nio.file.Files.java :

 // buffer size used for reading and writing private static final int BUFFER_SIZE = 8192; /** * Reads all bytes from an input stream and writes them to an output stream. */ private static long copy(InputStream source, OutputStream sink) throws IOException { long nread = 0L; byte[] buf = new byte[BUFFER_SIZE]; int n; while ((n = source.read(buf)) > 0) { sink.write(buf, 0, n); nread += n; } return nread; } 

Utilizzando Java7 e try-with-resources , viene fornita con una versione semplificata e leggibile.

  try(InputStream inputStream = new FileInputStream("C:\\mov.mp4"); OutputStream outputStream = new FileOutputStream("D:\\mov.mp4")){ byte[] buffer = new byte[10*1024]; for (int length; (length = inputStream.read(buffer)) != -1; ){ outputStream.write(buffer, 0, length); } }catch (FileNotFoundException exception){ exception.printStackTrace(); }catch (IOException ioException){ ioException.printStackTrace(); } 

Per coloro che usano il framework Spring c’è una class StreamUtils utile:

 StreamUtils.copy(in, out); 

Quanto sopra non chiude i flussi. Se si desidera che i flussi vengano chiusi dopo la copia, utilizzare invece la class FileCopyUtils :

 FileCopyUtils.copy(in, out); 

Usa la class Util di Commons Net:

 import org.apache.commons.net.io.Util; ... Util.copyStream(in, out); 

Penso che sia meglio utilizzare un buffer di grandi dimensioni, perché la maggior parte dei file è superiore a 1024 byte. Inoltre è una buona pratica controllare il numero di byte letti per essere positivi.

 byte[] buffer = new byte[4096]; int n; while ((n = in.read(buffer)) > 0) { out.write(buffer, 0, n); } out.close(); 

Un frammento IMHO più minimale (che circoscrive anche la variabile di lunghezza):

 byte[] buffer = new byte[2048]; for (int n = in.read(buffer); n >= 0; n = in.read(buffer)) out.write(buffer, 0, n); 

Come nota a margine, non capisco perché più persone non usano un ciclo for , optando invece per un while con un’espressione assign-and-test che viene considerata da alcuni uno stile “povero”.

Ecco come sto procedendo con il ciclo più semplice.

 private void copy(final InputStream in, final OutputStream out) throws IOException { final byte[] b = new byte[8192]; for (int r; (r = in.read(b)) != -1;) { out.write(b, 0, r); } } 

PipedInputStream e PipedOutputStream possono essere di qualche utilità, in quanto è ansible connettersi l’uno all’altro.

Un altro ansible candidato sono le utility I / O di Guava:

http://code.google.com/p/guava-libraries/wiki/IOExplained

Ho pensato di usarli poiché Guava è già immensamente utile nel mio progetto, piuttosto che aggiungere un’altra libreria per una funzione.

Prova Cactoos :

 new LengthOf(new TeeInput(input, output)).value(); 

Maggiori dettagli qui: http://www.yegor256.com/2017/06/22/object-oriented-input-output-in-cactoos.html

 public static boolean copyFile(InputStream inputStream, OutputStream out) { byte buf[] = new byte[1024]; int len; long startTime=System.currentTimeMillis(); try { while ((len = inputStream.read(buf)) != -1) { out.write(buf, 0, len); } long endTime=System.currentTimeMillis()-startTime; Log.v("","Time taken to transfer all bytes is : "+endTime); out.close(); inputStream.close(); } catch (IOException e) { return false; } return true; } 

puoi usare questo metodo

 public static void copyStream(InputStream is, OutputStream os) { final int buffer_size=1024; try { byte[] bytes=new byte[buffer_size]; for(;;) { int count=is.read(bytes, 0, buffer_size); if(count==-1) break; os.write(bytes, 0, count); } } catch(Exception ex){} }