Wie konvertiere ich einen OutputStream in einen InputStream?

Lesezeit: 11 Minuten

Benutzeravatar von Waypoint
Wegpunkt

Ich bin in der Phase der Entwicklung, wo ich zwei Module habe und von einem habe ich eine Ausgabe als OutputStreamund eine zweite, die nur akzeptiert InputStream.

Weißt du, wie man konvertiert OutputStream Zu InputStream (nicht umgekehrt, ich meine wirklich so) damit ich diese beiden Teile verbinden kann?

  • Siehe stackoverflow.com/questions/1225909/…

    – Bala R

    25. April 2011 um 13:14 Uhr

  • @c0mrade, der Op will so etwas wie IOUtils.copy, nur in die andere Richtung. Wenn jemand in einen OutputStream schreibt, wird er für jemand anderen zur Verwendung in einem InputStream verfügbar. Dies ist im Grunde das, was PipedOutputStream/PipedInputStream tun. Leider können die Piped Streams nicht aus anderen Streams erstellt werden.

    – MeBigFatGuy

    25. April 2011 um 13:17 Uhr


  • also ist der PipedOutputStream/PipedInputStream die Lösung?

    – Wegpunkt

    25. April 2011 um 13:19 Uhr

  • Damit PipedStreams in Ihrem Fall funktioniert, müsste Ihr OutputStream grundsätzlich wie folgt aufgebaut sein new YourOutputStream(thePipedOutputStream) Und new YourInputStream(thePipedInputStream) Das ist wahrscheinlich nicht die Art und Weise, wie Ihr Stream funktioniert. Also ich glaube nicht, dass das die Lösung ist.

    – MeBigFatGuy

    25. April 2011 um 13:40 Uhr

Mikehos Benutzeravatar
Mikeho

Es scheint viele Links und ähnliches zu geben, aber keinen tatsächlichen Code, der Pipes verwendet. Der Vorteil der Verwendung java.io.PipedInputStream Und java.io.PipedOutputStream ist, dass es keinen zusätzlichen Speicherverbrauch gibt. ByteArrayOutputStream.toByteArray() gibt eine Kopie des ursprünglichen Puffers zurück, was bedeutet, dass Sie jetzt zwei Kopien davon haben, was auch immer Sie im Speicher haben. Dann schreiben Sie an eine InputStream bedeutet, dass Sie jetzt drei Kopien der Daten haben.

Der Code mit lambdas (Hutspitze an @John Manko aus den Kommentaren):

PipedInputStream in = new PipedInputStream();
final PipedOutputStream out = new PipedOutputStream(in);
// in a background thread, write the given output stream to the
// PipedOutputStream for consumption
new Thread(() -> {originalOutputStream.writeTo(out);}).start();

Eine Sache, die @John Manko angemerkt hat, ist, dass in bestimmten Fällen, wenn Sie keine Kontrolle über die Erstellung der Ausgabestromkönnen Sie in eine Situation geraten, in der der Ersteller die Ausgabestrom widersprechen vorzeitig. Wenn Sie die bekommen ClosedPipeExceptiondann sollten Sie versuchen, die Konstruktoren zu invertieren:

PipedInputStream in = new PipedInputStream(out);
new Thread(() -> {originalOutputStream.writeTo(out);}).start();

Beachten Sie, dass Sie die Konstruktoren auch für die folgenden Beispiele invertieren können.

Danke auch an @AlexK für die Korrektur mit dem Starten von a Thread anstatt einfach loszulegen Runnable.


Der Code mit try-with-resources:

// take the copy of the stream and re-write it to an InputStream
PipedInputStream in = new PipedInputStream();
    new Thread(new Runnable() {
        public void run () {
            // try-with-resources here
            // putting the try block outside the Thread will cause the
            // PipedOutputStream resource to close before the Runnable finishes
            try (final PipedOutputStream out = new PipedOutputStream(in)) {
                // write the original OutputStream to the PipedOutputStream
                // note that in order for the below method to work, you need
                // to ensure that the data has finished writing to the
                // ByteArrayOutputStream
                originalByteArrayOutputStream.writeTo(out);
            }
            catch (IOException e) {
                // logging and exception handling should go here
            }
        }
    }).start();

Der ursprüngliche Code, den ich geschrieben habe:

// take the copy of the stream and re-write it to an InputStream
PipedInputStream in = new PipedInputStream();
final PipedOutputStream out = new PipedOutputStream(in);
new Thread(new Runnable() {
    public void run () {
        try {
            // write the original OutputStream to the PipedOutputStream
            // note that in order for the below method to work, you need
            // to ensure that the data has finished writing to the
            // ByteArrayOutputStream
            originalByteArrayOutputStream.writeTo(out);
        }
        catch (IOException e) {
            // logging and exception handling should go here
        }
        finally {
            // close the PipedOutputStream here because we're done writing data
            // once this thread has completed its run
            if (out != null) {
                // close the PipedOutputStream cleanly
                out.close();
            }
        }   
    }
}).start();

Dieser Code geht davon aus, dass die originalByteArrayOutputStream ist ein ByteArrayOutputStream da dies normalerweise der einzige verwendbare Ausgabestrom ist, es sei denn, Sie schreiben in eine Datei. Das Tolle daran ist, dass es, da es sich in einem separaten Thread befindet, auch parallel arbeitet, sodass alles, was Ihren Eingabestream verbraucht, auch aus Ihrem alten Ausgabestream gestreamt wird. Das ist vorteilhaft, da der Puffer kleiner bleiben kann und Sie weniger Latenz und weniger Speicherverbrauch haben.

Wenn Sie keine haben ByteArrayOutputStreamdann anstatt zu verwenden writeTo()müssen Sie eine der verwenden write() Methoden in der java.io.OutputStream Klasse oder eine der anderen Methoden, die in einer Unterklasse verfügbar sind.

  • Ich habe dafür gestimmt, aber es ist besser zu bestehen out Zu in‘s-Konstruktor, andernfalls erhalten Sie möglicherweise eine geschlossene Pipe-Ausnahme in aufgrund der Rennbedingungen (die ich erlebt habe). Verwenden von Java 8-Lambdas: PipedInputStream in = new PipedInputStream(out); ((Runnable)() -> {originalOutputStream.writeTo(out);}).run(); return in;

    – John Manko

    13. September 2015 um 5:07 Uhr


  • Nein, mein Fall stammt davon, dass ich PDFs in Mongo GridFS speichere und dann mit Jax-RS zum Client streame. MongoDB liefert einen OutputStream, aber Jax-RS benötigt einen InputStream. Meine Pfadmethode würde mit einem InputStream zum Container zurückkehren, bevor der OutputStream vollständig eingerichtet war, wie es scheint (vielleicht wurde der Puffer noch nicht zwischengespeichert). Wie auch immer, Jax-RS würde eine geschlossene Pipe-Ausnahme auf dem InputStream auslösen. Seltsam, aber das ist die Hälfte der Zeit passiert. Das Ändern des obigen Codes verhindert dies.

    – John Manko

    14. September 2015 um 21:12 Uhr


  • @JohnManko Ich habe mir das genauer angesehen und aus dem gesehen PipedInputStream Javadocs: Eine Pipe wird als defekt bezeichnet, wenn ein Thread, der Datenbytes für den verbundenen, per Pipe geleiteten Ausgabestrom bereitgestellt hat, nicht mehr aktiv ist. Ich vermute also, dass, wenn Sie das obige Beispiel verwenden, der Thread vorher abgeschlossen wird Jax-RS verbraucht den Eingabestrom. Gleichzeitig schaute ich mir die an MongoDB Javadocs. GridFSDBFile hat einen Eingabestrom, warum also nicht einfach diesen weitergeben Jax-RS?

    – Mikeho

    15. September 2015 um 16:44 Uhr

  • @DennisCheung Ja, natürlich. Nichts ist kostenlos, aber es wird sicherlich kleiner als eine 15-MB-Kopie sein. Zu den Optimierungen würde stattdessen die Verwendung eines Thread-Pools gehören, um die GC-Abwanderung mit konstanter Thread-/Objekterstellung zu reduzieren.

    – Mikeho

    27. Juli 2016 um 18:53 Uhr

  • Beachten Sie, dass PipedInputStream und PipedOutputStream beide in einem separaten Thread sein müssen, da es sonst ab einer bestimmten Größe zu einem Deadlock kommen kann (siehe Java doc: docs.oracle.com/javase/7/docs/api/java/io/PipedInputStream.html)

    – Elektropepi

    27. August 2020 um 12:47 Uhr


Benutzeravatar von Java Drinker
Java-Trinker

Ein OutputStream ist einer, in den Sie Daten schreiben. Wenn ein Modul eine OutputStreamist die Erwartung, dass am anderen Ende etwas gelesen wird.

Etwas, das einen aussetzt InputStreamauf der anderen Seite, zeigt an, dass Sie sich diesen Stream anhören müssen, und es wird Daten geben, die Sie lesen können.

Es ist also möglich, eine zu verbinden InputStream zu einem OutputStream

InputStream----read---> intermediateBytes[n] ----write----> OutputStream

Wie jemand erwähnte, ist dies das, was die copy() Methode aus IOUtils lässt dich machen. Es macht keinen Sinn, den anderen Weg zu gehen … hoffentlich macht das einen Sinn

AKTUALISIEREN:

Je mehr ich darüber nachdenke, desto mehr kann ich natürlich erkennen, dass dies tatsächlich eine Anforderung wäre. Ich kenne einige der erwähnten Kommentare Piped Input/Output-Streams, aber es gibt noch eine andere Möglichkeit.

Wenn der verfügbar gemachte Ausgabestrom a ByteArrayOutputStreamdann können Sie jederzeit den vollständigen Inhalt abrufen, indem Sie die anrufen toByteArray() Methode. Dann können Sie einen Input-Stream-Wrapper erstellen, indem Sie die verwenden ByteArrayInputStream Unterklasse. Diese beiden sind Pseudo-Streams, beide packen im Grunde nur ein Array von Bytes. Die Nutzung der Streams auf diese Weise ist also technisch möglich, aber für mich immer noch sehr seltsam …

  • copy() macht dies IS to OS gemäß API, ich brauche es, um es rückwärts zu machen

    – Wegpunkt

    25. April 2011 um 13:47 Uhr

  • Der Anwendungsfall ist sehr einfach: Stellen Sie sich vor, Sie haben eine Serialisierungsbibliothek (z. B. Serialisierung in JSON) und eine Transportschicht (z. B. Tomcat), die einen InputStream entgegennimmt. Sie müssen also den OutputStream von JSON über eine HTTP-Verbindung leiten, die von einem InputStream lesen möchte.

    – JBCP

    21. Februar 2014 um 17:28 Uhr

  • Dies ist beim Komponententest nützlich und Sie sind sehr pedantisch, wenn es darum geht, das Dateisystem nicht zu berühren.

    – Jon

    13. März 2014 um 9:26 Uhr

  • Der Kommentar von @JBCP ist genau richtig. Ein weiterer Anwendungsfall ist die Verwendung von PDFBox zum Erstellen von PDFs während einer HTTP-Anforderung. PDFBox verwendet einen OutputStream, um ein PDF-Objekt zu speichern, und die REST-API akzeptiert einen InputStream, um dem Client zu antworten. Daher ist ein OutputStream -> InputStream ein sehr realer Anwendungsfall.

    – John Manko

    12. September 2015 um 18:47 Uhr

  • “Sie können immer den vollständigen Inhalt abrufen, indem Sie die Methode toByteArray() aufrufen” Der Sinn der Verwendung von Streams besteht darin, nicht den gesamten Inhalt in den Speicher zu laden !!

    – Stapel übergelaufen

    8. April 2020 um 7:06 Uhr

Benutzeravatar von BorutT
BorutT

Da Eingabe- und Ausgabeströme nur Start- und Endpunkte sind, besteht die Lösung darin, Daten vorübergehend in einem Byte-Array zu speichern. Sie müssen also ein Zwischenprodukt erstellen ByteArrayOutputStreamaus der Sie erstellen byte[] die als Input für new verwendet wird ByteArrayInputStream.

public void doTwoThingsWithStream(InputStream inStream, OutputStream outStream){ 
  //create temporary bayte array output stream
  ByteArrayOutputStream baos = new ByteArrayOutputStream();
  doFirstThing(inStream, baos);
  //create input stream from baos
  InputStream isFromFirstData = new ByteArrayInputStream(baos.toByteArray()); 
  doSecondThing(isFromFirstData, outStream);
}

Ich hoffe es hilft.

ByteArrayOutputStream buffer = (ByteArrayOutputStream) aOutputStream;
byte[] bytes = buffer.toByteArray();
InputStream inputStream = new ByteArrayInputStream(bytes);

mckameys Benutzeravatar
Mckamey

Sie benötigen eine Zwischenklasse, die dazwischen puffert. Jedes Mal InputStream.read(byte[]...) aufgerufen wird, füllt die Pufferklasse das übergebene Byte-Array mit dem nächsten Chunk, der von übergeben wird OutputStream.write(byte[]...). Da die Größen der Chunks möglicherweise nicht gleich sind, muss die Adapterklasse eine bestimmte Menge speichern, bis sie genug hat, um den Lesepuffer zu füllen und/oder in der Lage zu sein, einen Pufferüberlauf zu speichern.

Dieser Artikel enthält eine schöne Aufschlüsselung einiger verschiedener Ansätze für dieses Problem:

http://blog.ostermiller.org/convert-java-outputstream-inputstream

  • danke @mckamey, die auf Circular Buffers basierende Methode ist genau das, was ich brauche!

    – Hui Wang

    7. April 2016 um 14:25 Uhr

Der easystream Die Open-Source-Bibliothek bietet direkte Unterstützung zum Konvertieren eines OutputStream in einen InputStream: http://io-tools.sourceforge.net/easystream/tutorial/tutorial.html

// create conversion
final OutputStreamToInputStream<Void> out = new OutputStreamToInputStream<Void>() {
    @Override
    protected Void doRead(final InputStream in) throws Exception {
           LibraryClass2.processDataFromInputStream(in);
           return null;
        }
    };
try {   
     LibraryClass1.writeDataToTheOutputStream(out);
} finally {
     // don't miss the close (or a thread would not terminate correctly).
     out.close();
}

Sie listen auch andere Optionen auf: http://io-tools.sourceforge.net/easystream/outputstream_to_inputstream/implementations.html

  • Schreibe die Daten die Daten in einen Speicherpuffer (ByteArrayOutputStream) hole das byteArray und lese es wieder mit einem ByteArrayInputStream. Dies ist der beste Ansatz, wenn Sie sicher sind, dass Ihre Daten in den Speicher passen.
  • Kopieren Sie Ihre Daten in eine temporäre Datei und lesen Sie sie zurück.
  • Verwenden Sie Pipes: Dies ist der beste Ansatz sowohl für die Speichernutzung als auch für die Geschwindigkeit (Sie können die Mehrkernprozessoren voll ausnutzen) und auch die von Sun angebotene Standardlösung.
  • Verwenden Sie InputStreamFromOutputStream und OutputStreamToInputStream aus der easystream-Bibliothek.

  • danke @mckamey, die auf Circular Buffers basierende Methode ist genau das, was ich brauche!

    – Hui Wang

    7. April 2016 um 14:25 Uhr

Ich hatte das gleiche Problem beim Konvertieren von a ByteArrayOutputStream zu einem ByteArrayInputStream und löste es mit einer abgeleiteten Klasse von ByteArrayOutputStream die in der Lage ist, a zurückzugeben ByteArrayInputStream die mit dem internen Puffer der initialisiert wird ByteArrayOutputStream. Auf diese Weise wird kein zusätzlicher Speicher verwendet und die ‘Konvertierung’ ist sehr schnell:

package info.whitebyte.utils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

/**
 * This class extends the ByteArrayOutputStream by 
 * providing a method that returns a new ByteArrayInputStream
 * which uses the internal byte array buffer. This buffer
 * is not copied, so no additional memory is used. After
 * creating the ByteArrayInputStream the instance of the
 * ByteArrayInOutStream can not be used anymore.
 * <p>
 * The ByteArrayInputStream can be retrieved using <code>getInputStream()</code>.
 * @author Nick Russler
 */
public class ByteArrayInOutStream extends ByteArrayOutputStream {
    /**
     * Creates a new ByteArrayInOutStream. The buffer capacity is
     * initially 32 bytes, though its size increases if necessary.
     */
    public ByteArrayInOutStream() {
        super();
    }

    /**
     * Creates a new ByteArrayInOutStream, with a buffer capacity of
     * the specified size, in bytes.
     *
     * @param   size   the initial size.
     * @exception  IllegalArgumentException if size is negative.
     */
    public ByteArrayInOutStream(int size) {
        super(size);
    }

    /**
     * Creates a new ByteArrayInputStream that uses the internal byte array buffer 
     * of this ByteArrayInOutStream instance as its buffer array. The initial value 
     * of pos is set to zero and the initial value of count is the number of bytes 
     * that can be read from the byte array. The buffer array is not copied. This 
     * instance of ByteArrayInOutStream can not be used anymore after calling this
     * method.
     * @return the ByteArrayInputStream instance
     */
    public ByteArrayInputStream getInputStream() {
        // create new ByteArrayInputStream that respects the current count
        ByteArrayInputStream in = new ByteArrayInputStream(this.buf, 0, this.count);

        // set the buffer of the ByteArrayOutputStream 
        // to null so it can't be altered anymore
        this.buf = null;

        return in;
    }
}

Ich habe das Zeug auf Github gestellt: https://github.com/nickrussler/ByteArrayInOutStream

  • Was ist, wenn der Inhalt nicht in den Puffer passt?

    – Vad1mo

    15. September 2014 um 9:20 Uhr

  • Dann sollten Sie erst gar keinen ByteArrayInputStream verwenden.

    – Nick Russler

    15. September 2014 um 9:39 Uhr

  • Diese Lösung hat alle Bytes im Speicher. Für kleine Dateien ist dies in Ordnung, aber dann können Sie auch getBytes() auf ByteArrayOutput Stream verwenden

    – Vad1mo

    15. September 2014 um 11:40 Uhr

  • Wenn du meinst toByteArray Dies würde dazu führen, dass der interne Puffer kopiert wird, was doppelt so viel Speicher benötigt wie mein Ansatz. Edit: Ah ich verstehe, für kleine Dateien funktioniert das natürlich..

    – Nick Russler

    15. September 2014 um 15:40 Uhr


  • Zeitverschwendung. ByteArrayOutputStream verfügt über eine writeTo-Methode, um Inhalte an einen anderen Ausgabestream zu übertragen

    – Tony BenBrahim

    17. Februar 2015 um 18:38 Uhr

1450020cookie-checkWie konvertiere ich einen OutputStream in einen InputStream?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy