Un modo semplice per scrivere il contenuto di un InputStream Java su un OutputStream

StackOverflow https://stackoverflow.com/questions/43157

  •  09-06-2019
  •  | 
  •  

Domanda

Sono stato sorpreso di scoprire oggi che non sono riuscito a rintracciare un modo semplice per scrivere il contenuto di un file InputStream ad OutputStream a Giava.Ovviamente, il codice del buffer dei byte non è difficile da scrivere, ma sospetto che mi manchi qualcosa che mi renderebbe la vita più semplice (e il codice più chiaro).

Quindi, dato un InputStream in e un OutputStream out, esiste 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);
}
È stato utile?

Soluzione

Se utilizzi Java 7, File (nella libreria standard) è l'approccio migliore:

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

Modificare:Ovviamente è utile solo quando crei uno tra InputStream o OutputStream dal file.Utilizzo file.toPath() per ottenere il percorso dal file.

Per scrivere in un file esistente (ad es.uno creato con File.createTempFile()), dovrai superare il file REPLACE_EXISTING opzione di copia (altrimenti FileAlreadyExistsException viene lanciato):

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

Altri suggerimenti

Come menzionato da WMR, org.apache.commons.io.IOUtils da 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?

Giava9

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

public long transferTo(OutputStream out) throws IOException

Come il documentazione stati, transferTo Volere:

Legge tutti i byte di questo flusso di input e scrive i byte al flusso di output indicato nell'ordine in cui vengono letti.Al ritorno, questo flusso di input sarà alla fine del flusso.Questo metodo non chiude nessuno dei flussi.

Questo metodo può bloccare la lettura indefinitamente dal flusso di input o scrivere al flusso di output.Il comportamento per il caso in cui il flusso di input e/o output è chiuso in modo asincrono, o il thread interrotto durante il trasferimento, è altamente specifico del flusso di input e dell'uscita e quindi non specificato

Quindi, per scrivere il contenuto di un file Java InputStream ad OutputStream, tu puoi scrivere:

input.transferTo(output);

Penso che funzionerà, ma assicurati di provarlo...piccolo "miglioramento", ma potrebbe costare un po' in termini di leggibilità.

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

Usando Guava ByteStreams.copy():

ByteStreams.copy(inputStream, outputStream);

Funzione semplice

Se ti serve solo per scrivere un file InputStream ad a File allora puoi usare 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 dovrebbe essere usato solo quando hai più thread, come notato dal Javadoc.

Inoltre, tieni presente che i flussi di input e di output non racchiudono alcuna interruzione del thread IOExceptionS...Quindi, dovresti considerare di incorporare una politica di interruzione nel tuo 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'aggiunta utile se prevedi di utilizzare questa API per copiare grandi volumi di dati o dati da flussi che rimangono bloccati per un tempo intollerabilmente lungo.

IL JDK utilizza lo stesso codice, quindi sembra che non esista un modo "più semplice" senza goffe librerie di terze parti (che probabilmente comunque non fanno nulla di diverso).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;
    }

Per chi usa Quadro primaverile c'è un utile StreamUtils classe:

StreamUtils.copy(in, out);

Quanto sopra non chiude i flussi.Se vuoi che i flussi vengano chiusi dopo la copia, usa FileCopyUtils classe invece:

FileCopyUtils.copy(in, out);

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, ha anche molte altre cose utili, per cui l'IMO dovrebbe effettivamente far parte del JDK...

Utilizzando Java7 e provare con le risorse, viene fornito 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();
    }

Utilizza la classe Utili di Commons Net:

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

Ecco come sto andando con il ciclo for 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);
    }
}

Uno snippet IMHO più minimale (che copre anche in modo più ristretto la variabile 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 a for loop, optando invece per a while con un'espressione di assegnazione e prova che è considerata da alcuni come uno stile "povero".

Penso che sia meglio usare un buffer grande, perché la maggior parte dei file sono più grandi di 1024 byte.Inoltre è buona norma verificare che il numero di byte letti sia positivo.

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

PipedInputStream e PipedOutputStream possono essere di qualche utilità, poiché puoi connetterti l'uno all'altro.

Un altro possibile candidato sono le utility Guava I/O:

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

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

Io uso BufferedInputStream E BufferedOutputStream per rimuovere la semantica del buffer dal codice

try (OutputStream out = new BufferedOutputStream(...);
     InputStream in   = new BufferedInputStream(...))) {
  int ch;
  while ((ch = in.read()) != -1) {
    out.write(ch);
  }
}
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;
}

Tentativo Cattoo:

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

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

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){}
 }
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top