31 votes

Meilleur moyen de faire passer un flux d'entrée (InputStream) à un flux de sortie (OutputStream)

J'essayais de trouver la meilleure façon de transformer l'InputStream en OutputStream. Je n'ai pas la possibilité d'utiliser d'autres bibliothèques comme Apache IO. Voici l'extrait et le résultat.

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.FileChannel;

public class Pipe {
    public static void main(String[] args) throws Exception {

        for(PipeTestCase testCase : testCases) {
            System.out.println(testCase.getApproach());
            InputStream is = new FileInputStream("D:\\in\\lft_.txt");
            OutputStream os = new FileOutputStream("D:\\in\\out.txt");

            long start = System.currentTimeMillis();            
            testCase.pipe(is, os);
            long end = System.currentTimeMillis();

            System.out.println("Execution Time = " + (end - start) + " millis");
            System.out.println("============================================");

            is.close();
            os.close();
        }

    }

    private static PipeTestCase[] testCases = {

        new PipeTestCase("Fixed Buffer Read") {         
            @Override
            public void pipe(InputStream is, OutputStream os) throws IOException {
                byte[] buffer = new byte[1024];
                while(is.read(buffer) > -1) {
                    os.write(buffer);   
                }
            }
        },

        new PipeTestCase("dynamic Buffer Read") {           
            @Override
            public void pipe(InputStream is, OutputStream os) throws IOException {
                byte[] buffer = new byte[is.available()];
                while(is.read(buffer) > -1) {
                    os.write(buffer);   
                    buffer = new byte[is.available() + 1];
                }
            }
        },

        new PipeTestCase("Byte Read") {         
            @Override
            public void pipe(InputStream is, OutputStream os) throws IOException {
                int c; 
                while((c = is.read()) > -1) {
                    os.write(c);    
                }
            }
        }, 

        new PipeTestCase("NIO Read") {          
            @Override
            public void pipe(InputStream is, OutputStream os) throws IOException {
                FileChannel source      = ((FileInputStream) is).getChannel(); 
                FileChannel destnation  = ((FileOutputStream) os).getChannel();
                destnation.transferFrom(source, 0, source.size());
            }
        }, 

    };
}

abstract class PipeTestCase {
    private String approach; 
    public PipeTestCase( final String approach) {
        this.approach = approach;           
    }

    public String getApproach() {
        return approach;
    }

    public abstract void pipe(InputStream is, OutputStream os) throws IOException;
}

Sortie (fichier d'entrée de ~4MB) :

Fixed Buffer Read
Execution Time = 71 millis
============================================
dynamic Buffer Read
Execution Time = 167 millis
============================================
Byte Read
Execution Time = 29124 millis
============================================
NIO Read
Execution Time = 125 millis
============================================

La fonction "lecture dynamique de la mémoire tampon" utilise available() méthode. Mais elle n'est pas fiable selon la documentation de Java.

Il n'est jamais correct d'utiliser la valeur de retour de cette méthode pour allouer un tampon destiné à contenir toutes les données de ce flux.

La lecture d'octets semble être très lente.

Donc "Fixed Buffer Read" est la meilleure option pour le tuyau ? Qu'en pensez-vous ?

18voto

Dariusz Points 8058

Java 9

Depuis Java 9, on peut utiliser cette méthode à partir de InputStream :

public long transferTo(OutputStream out) throws IOException

Pré Java 9

A Une seule phrase de apache commons :

IOUtils.copy(inputStream, outputStream);

Documentation ici . Il existe de multiples copy avec des paramètres différents. Il est également possible de spécifier la taille du tampon.

14voto

paulsm4 Points 39422

Je suis tombé sur ceci, et la lecture finale peut causer des problèmes.

CHANGEMENT SUGGÉRÉ :

public void pipe(InputStream is, OutputStream os) throws IOException {
  int n;
  byte[] buffer = new byte[1024];
  while((n = is.read(buffer)) > -1) {
    os.write(buffer, 0, n);   // Don't allow any extra bytes to creep in, final write
  }
 os.close ();

Je suis également d'accord pour dire que 16384 est probablement une meilleure taille de tampon fixe que 1024.

A MON AVIS...

12voto

Mike Q Points 9660

Je dirais qu'une taille de tampon fixe est la meilleure/la plus facile à comprendre. Cependant, il y a quelques problèmes.

  • Vous écrivez l'intégralité du tampon sur le flux de sortie à chaque fois. Pour le dernier bloc, la lecture peut avoir lu < 1024 octets, vous devez donc en tenir compte lors de l'écriture (en fait, vous n'écrivez que le nombre d'octets renvoyé par la commande read()

  • Dans le cas d'un tampon dynamique, vous utilisez available() . Ce n'est pas un appel d'API très fiable. Je ne suis pas sûr que dans ce cas, à l'intérieur d'une boucle, ce soit correct, mais je ne serais pas surpris qu'il soit implémenté de manière sous-optimale dans certaines implémentations de InputStream.

  • Le dernier cas que vous coulez à FileInputStream . Si vous avez l'intention d'en faire un usage général, vous ne pouvez pas utiliser cette approche.

8voto

Radu Simionescu Points 687

java.io contient PipedInputStream y PipedOutputStream

PipedInputStream input = new PipedInputStream();
PipedOutputStream output = new PipedOutputStream (input);

écrire à l'entrée et il sera visible dans la sortie comme une Outputstream . Les choses peuvent aussi fonctionner dans l'autre sens

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