472 votes

Une façon simple d'écrire le contenu d'un InputStream Java dans un OutputStream.

J'ai été surpris de constater aujourd'hui que je n'ai pas réussi à trouver un moyen simple d'écrire le contenu d'un fichier de type InputStream à un OutputStream en Java. De toute évidence, le code du tampon d'octets n'est pas difficile à écrire, mais je pense qu'il me manque quelque chose qui me faciliterait la vie (et rendrait le code plus clair).

Donc, étant donné un InputStream in et un OutputStream out Y a-t-il une façon 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);
}

1 votes

Vous avez mentionné dans un commentaire que c'est pour une application mobile. S'agit-il d'une application Android native ? Si oui, faites-le moi savoir et je posterai une autre réponse (cela peut être fait en une seule ligne de code dans Android).

402voto

Mikezx6r Points 6541

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 cherchez.

Donc, vous avez:

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

... dans votre code.

Y a-t-il une raison pour laquelle vous évitez IOUtils ?

176 votes

J'évite de l'utiliser pour l'application mobile que je suis en train de créer, car cela reviendrait à quintupler la taille de l'application pour économiser 5 lignes de code.

37 votes

Il est peut-être utile de mentionner que in y out doit être fermé à la fin du code dans un bloc finally.

24 votes

@basZero Ou en utilisant un bloc try with resources.

333voto

user1079877 Points 703

Si vous utilisez Java 7, Fichiers (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)

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

Pour écrire dans un fichier existant (par exemple, un fichier créé avec la commande File.createTempFile() ), vous devrez passer l'option REPLACE_EXISTING option de copie (sinon FileAlreadyExistsException est lancée) :

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

27 votes

Je ne pense pas que cela résolve réellement le problème puisqu'une extrémité est un chemin. Alors que vous pouvez obtenir un chemin pour un fichier, pour autant que je sache, vous ne pouvez pas en obtenir un pour un flux générique (par exemple, un flux sur le réseau).

2 votes

+1 s'est tout de même avéré utile dans mon cas, car j'ai un vrai fichier du côté de la sortie !

0 votes

En ce qui me concerne, la première méthode que vous mentionnez n'est pas disponible avec cette signature. Vous devez ajouter le CopyOptions paramètre.

107voto

Mike Stone Points 21293

Je pense que cela fonctionnera, mais assurez-vous de le tester... une "amélioration" mineure, mais cela pourrait coûter un peu à la lisibilité.

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

27 votes

Je suggère un tampon d'au moins 10KB à 100KB. Ce n'est pas beaucoup et cela peut accélérer considérablement la copie de grandes quantités de données.

0 votes

Cela a fonctionné pour moi lors de l'écriture d'un fichier. Je n'avais pas réalisé que je devais récupérer la longueur réelle lue dans le flux d'entrée et l'utiliser pour la sortie dans un fichier.

7 votes

Vous pourriez vouloir dire while(len > 0) au lieu de != -1 car cette dernière pourrait également renvoyer 0 lors de l'utilisation de la fonction read(byte b[], int off, int len) -qui déclenche une exception @ out.write

57voto

Andrejs Points 4235

En utilisant la technologie de la Goyave ByteStreams.copy() :

ByteStreams.copy(inputStream, outputStream);

11 votes

N'oubliez pas de fermer les flux après cela !

0 votes

C'est la meilleure réponse si vous utilisez déjà Guava qui est devenu indispensable pour moi.

1 votes

@Hong Vous devriez utiliser Files.copy autant que possible. Utilisez ByteStreams.copy uniquement si les deux flux ne sont pas des FileInputStream/FileOutputStream.

29voto

Jordan LaPrise Points 111

Fonction simple

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

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

4 votes

Excellente fonction, merci. Auriez-vous besoin de mettre le close() Les appels finally des blocs, cependant ?

0 votes

@JoshPinter Ça ne ferait pas de mal.

3 votes

Vous devriez probablement à la fois inclure un bloc final et ne pas avaler les exceptions dans une implémentation réelle. De plus, la fermeture d'un InputStream passé à une méthode est parfois inattendue par la méthode appelante, il faut donc se demander si c'est le comportement que l'on souhaite.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X