web-dev-qa-db-fra.com

Le moyen le plus efficace de créer InputStream à partir de OutputStream

Cette page: http://blog.ostermiller.org/convert-Java-outputstream-inputstream décrit comment créer un InputStream à partir de OutputStream:

new ByteArrayInputStream(out.toByteArray())

D'autres alternatives consistent à utiliser PipedStreams et de nouveaux threads, ce qui est fastidieux.

Je n'aime pas l'idée de copier plusieurs mégaoctets dans un nouveau tableau d'octets mémoire. Y a-t-il une bibliothèque qui le fait plus efficacement?

MODIFIER:

Sur les conseils de Laurence Gonsalves, j’ai essayé PipedStreams et il s’est avéré qu’ils ne sont pas si difficiles à gérer. Voici l'exemple de code dans clojure:

(defn #^PipedInputStream create-pdf-stream [pdf-info]
  (let [in-stream (new PipedInputStream)
        out-stream (PipedOutputStream. in-stream)]
    (.start (Thread. #(;Here you write into out-stream)))
    in-stream))
79
Vagif Verdi

Si vous ne voulez pas copier toutes les données dans un tampon en mémoire en même temps, votre code utilisant OutputStream (le producteur) et le code utilisant InputStream (le consommateur ) alternent dans le même thread ou opèrent simultanément dans deux threads distincts. Les faire fonctionner dans le même thread est probablement beaucoup plus compliqué que d'utiliser deux threads séparés, c'est beaucoup plus sujet aux erreurs (vous devrez vous assurer que le consommateur jamais bloque l'attente d'entrée ou vous ' Dans l’intervalle, il faudrait que le producteur et le consommateur se retrouvent dans la même boucle, ce qui semble bien trop couplé.

Alors utilisez un deuxième fil. Ce n'est vraiment pas si compliqué. La page que vous avez liée avait un exemple parfait:

  PipedInputStream in = new PipedInputStream();
  PipedOutputStream out = new PipedOutputStream(in);
  new Thread(
    new Runnable(){
      public void run(){
        class1.putDataOnOutputStream(out);
      }
    }
  ).start();
  class2.processDataFromInputStream(in);
67
Laurence Gonsalves

Il existe une autre bibliothèque Open Source appelée EasyStream qui traite les tubes et les threads de manière transparente. Ce n'est pas vraiment compliqué si tout se passe bien. Des problèmes surviennent lorsque (en regardant l'exemple de Laurence Gonsalves)

class1.putDataOnOutputStream (out);

Lance une exception. Dans cet exemple, le thread se termine simplement et l'exception est perdue, tandis que le InputStream extérieur peut être tronqué.

Easystream traite de la propagation des exceptions et d'autres problèmes graves que je corrige depuis environ un an. (Je suis le responsable de la bibliothèque: évidemment, ma solution est la meilleure;)) Voici un exemple d'utilisation:

final InputStreamFromOutputStream<String> isos = new InputStreamFromOutputStream<String>(){
 @Override
 public String produce(final OutputStream dataSink) throws Exception {
   /*
    * call your application function who produces the data here
    * WARNING: we're in another thread here, so this method shouldn't 
    * write any class field or make assumptions on the state of the outer class. 
    */
   return produceMydata(dataSink)
 }
};

Il y a aussi un Nice introduction où toutes les autres façons de convertir un OutputStream en InputStream sont expliquées. Ça vaut le coup d'oeil.

15
Gab

Une solution simple qui évite de copier le tampon consiste à créer un objet spécifique ByteArrayOutputStream:

public class CopyStream extends ByteArrayOutputStream {
    public CopyStream(int size) { super(size); }

    /**
     * Get an input stream based on the contents of this output stream.
     * Do not use the output stream after calling this method.
     * @return an {@link InputStream}
     */
    public InputStream toInputStream() {
        return new ByteArrayInputStream(this.buf, 0, this.count);
    }
}

Écrivez si nécessaire dans le flux de sortie ci-dessus, puis appelez toInputStream pour obtenir un flux d’entrée sur la mémoire tampon sous-jacente. Considérez le flux de sortie comme fermé après ce point.

9
Eron Wright

Je pense que le meilleur moyen de connecter InputStream à un OutputStream est d'utiliser flux acheminés - disponible dans le package Java.io, comme suit:

// 1- Define stream buffer
private static final int PIPE_BUFFER = 2048;

// 2 -Create PipedInputStream with the buffer
public PipedInputStream inPipe = new PipedInputStream(PIPE_BUFFER);

// 3 -Create PipedOutputStream and bound it to the PipedInputStream object
public PipedOutputStream outPipe = new PipedOutputStream(inPipe);

// 4- PipedOutputStream is an OutputStream, So you can write data to it
// in any way suitable to your data. for example:
while (Condition) {
     outPipe.write(mByte);
}

/*Congratulations:D. Step 4 will write data to the PipedOutputStream
which is bound to the PipedInputStream so after filling the buffer
this data is available in the inPipe Object. Start reading it to
clear the buffer to be filled again by the PipedInputStream object.*/

À mon avis, ce code présente deux avantages principaux:

1 - Il n’ya pas de consommation supplémentaire de mémoire à l’exception du tampon.

2 - Vous n'avez pas besoin de gérer manuellement la mise en file d'attente des données

6
Mostafa Abdellateef

J'essaie généralement d'éviter de créer un thread séparé en raison du risque accru de blocage, de la difficulté accrue à comprendre le code et des problèmes liés au traitement des exceptions.

Voici ma solution proposée: un ProducerInputStream qui crée du contenu en morceaux par des appels répétés à productChunk ():

public abstract class ProducerInputStream extends InputStream {

    private ByteArrayInputStream bin = new ByteArrayInputStream(new byte[0]);
    private ByteArrayOutputStream bout = new ByteArrayOutputStream();

    @Override
    public int read() throws IOException {
        int result = bin.read();
        while ((result == -1) && newChunk()) {
            result = bin.read();
        }
        return result;
    }

    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        int result = bin.read(b, off, len);
        while ((result == -1) && newChunk()) {
            result = bin.read(b, off, len);
        }
        return result;
    }

    private boolean newChunk() {
        bout.reset();
        produceChunk(bout);
        bin = new ByteArrayInputStream(bout.toByteArray());
        return (bout.size() > 0);
    }

    public abstract void produceChunk(OutputStream out);

}
1
Mark