Un moyen simple d'écrire le contenu d'un Java InputStream dans un OutputStream

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

  •  09-06-2019
  •  | 
  •  

Question

J'ai été surpris de constater aujourd'hui que je ne parvenais pas à trouver un moyen simple d'écrire le contenu d'un InputStream à un OutputStream en Java.Évidemment, le code du tampon d'octets n'est pas difficile à écrire, mais je soupçonne qu'il me manque juste quelque chose qui me faciliterait la vie (et le code plus clair).

Donc, étant donné un InputStream in Et un OutputStream out, existe-t-il un moyen plus simple d'écrire ce qui suit ?

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

La solution

Si vous utilisez Java 7, Des dossiers (dans la bibliothèque standard) est la meilleure approche :

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

Modifier:Bien sûr, c'est simplement utile lorsque vous créez un InputStream ou un OutputStream à partir d'un fichier.Utiliser file.toPath() pour obtenir le chemin du fichier.

Pour écrire dans un fichier existant (par ex.un créé avec File.createTempFile()), vous devrez réussir le REPLACE_EXISTING option de copie (sinon FileAlreadyExistsException Est lancé):

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

Autres conseils

Comme WMR l'a mentionné, org.apache.commons.io.IOUtils d'Apache a une méthode appelée copy(InputStream,OutputStream) qui fait exactement ce que vous recherchez.

Donc, vous avez :

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

...dans votre code.

Y a-t-il une raison pour laquelle tu évites IOUtils?

Java9

Depuis Java 9, InputStream fournit une méthode appelée transferTo avec la signature suivante :

public long transferTo(OutputStream out) throws IOException

Comme le Documentation États, transferTo volonté:

Lit tous les octets de ce flux d'entrée et écrit les octets au flux de sortie donné dans l'ordre où ils sont lus.À retour, ce flux d'entrée sera à la fin du flux.Cette méthode ne ferme aucun flux.

Cette méthode peut bloquer indéfiniment la lecture du flux d'entrée ou l'écriture au flux de sortie.Le comportement du cas où le flux d'entrée et / ou de sortie est fermé de manière asynchrone, ou le thread interrompu pendant le transfert, est fortement spécifique au flux d'entrée et de sortie, et donc non spécifié

Donc, pour écrire le contenu d'un Java InputStream à un OutputStream, tu peux écrire:

input.transferTo(output);

Je pense que cela fonctionnera, mais assurez-vous de le tester..."amélioration" mineure, mais cela pourrait être un peu un coût en termes de lisibilité.

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

Utiliser des goyaves ByteStreams.copy():

ByteStreams.copy(inputStream, outputStream);

Fonction simple

Si vous n'en avez besoin que pour rédiger un InputStream à un File alors vous pouvez utiliser cette fonction simple :

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 et PipedOutputStream ne doit être utilisé que lorsque vous avez plusieurs threads, comme noté par le Javadoc.

Notez également que les flux d'entrée et les flux de sortie n'encapsulent aucune interruption de thread avec IOExceptions...Vous devriez donc envisager d’incorporer une politique d’interruption à votre code :

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();
    }
}

Ce serait un ajout utile si vous prévoyez d'utiliser cette API pour copier de gros volumes de données ou des données provenant de flux bloqués pendant une période intolérablement longue.

Le JDK utilise le même code, il semble donc qu'il n'y ait pas de moyen "plus simple" sans bibliothèques tierces encombrantes (qui ne font probablement rien de différent de toute façon).Ce qui suit est directement copié de 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;
    }

Pour ceux qui utilisent Cadre de printemps il y a un utile StreamUtils classe:

StreamUtils.copy(in, out);

Ce qui précède ne ferme pas les flux.Si vous souhaitez que les flux soient fermés après la copie, utilisez FichierCopieUtils classe à la place :

FileCopyUtils.copy(in, out);

Il n'y a aucun moyen de faire cela beaucoup plus facilement avec les méthodes JDK, mais comme Apocalisp l'a déjà noté, vous n'êtes pas le seul à avoir cette idée :Vous pourriez utiliser IOUtils depuis Jakarta Commons IO, il contient également beaucoup d'autres choses utiles, que l'OMI devrait en fait faire partie du JDK...

Utiliser Java7 et essayer avec les ressources, est livré avec une version simplifiée et lisible.

    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();
    }

Utilisez la classe Util de Commons Net :

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

Voici comment je fais avec la boucle for la plus simple.

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);
    }
}

Un extrait à mon humble avis plus minimal (qui couvre également plus étroitement la variable de longueur) :

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

En passant, je ne comprends pas pourquoi plus de gens n'utilisent pas un for boucle, optant plutôt pour une while avec une expression d'attribution et de test qui est considérée par certains comme un style « médiocre ».

Je pense qu'il est préférable d'utiliser un tampon volumineux, car la plupart des fichiers font plus de 1024 octets.C'est également une bonne pratique de vérifier que le nombre d'octets lus est positif.

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

PipedInputStream et PipedOutputStream peuvent être utiles, car vous pouvez vous connecter l'un à l'autre.

Un autre candidat possible sont les utilitaires Guava I/O :

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

J'ai pensé les utiliser puisque Guava est déjà extrêmement utile dans mon projet, plutôt que d'ajouter une autre bibliothèque pour une fonction.

j'utilise BufferedInputStream et BufferedOutputStream pour supprimer la sémantique de mise en mémoire tampon du code

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;
}

Essayer Cactus:

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

Plus de détails ici : http://www.yegor256.com/2017/06/22/object-oriented-input-output-in-cactoos.html

tu peux utiliser cette méthode

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){}
 }
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top