Un moyen simple d'écrire le contenu d'un Java InputStream dans un OutputStream
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);
}
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 IOException
s...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){}
}