Wie konvertiere ich OutputStream in InputStream?

337

Ich bin in der Entwicklungsphase, wo ich zwei Module habe und von einem habe ich als OutputStreamund als zweites ausgegeben , was nur akzeptiert wird InputStream. Wissen Sie , wie zu konvertieren , OutputStreamum InputStream(nicht umgekehrt, ich meine wirklich so) , dass ich in der Lage sein wird , diese beiden Teile zu verbinden?

Vielen Dank

Wegpunkt
quelle
3
@ c0mrade, die Operation will so etwas wie IOUtils.copy, nur in die andere Richtung. Wenn jemand in einen OutputStream schreibt, kann er von einem anderen Benutzer in einem InputStream verwendet werden. Dies ist im Grunde das, was PipedOutputStream / PipedInputStream tun. Leider können die Piped-Streams nicht aus anderen Streams erstellt werden.
MeBigFatGuy
Also ist der PipedOutputStream / PipedInputStream die Lösung?
Wegpunkt
Grundsätzlich müsste Ihr OutputStream so aufgebaut sein, dass PipedStreams in Ihrem Fall funktioniert, new YourOutputStream(thePipedOutputStream)und new YourInputStream(thePipedInputStream)dies ist wahrscheinlich nicht die Art und Weise, wie Ihr Stream funktioniert. Ich denke also nicht, dass dies die Lösung ist.
MeBigFatGuy

Antworten:

109

An OutputStreamist einer, in den Sie Daten schreiben. Wenn ein Modul ein verfügbar macht OutputStream, wird erwartet, dass am anderen Ende etwas gelesen wird.

Etwas, das ein aufdeckt InputStream, zeigt andererseits an, dass Sie diesen Stream anhören müssen, und es gibt Daten, die Sie lesen können.

So ist es möglich, ein InputStreammit einem zu verbindenOutputStream

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

Wie jemand sagte, können Sie dies mit der copy()Methode von IOUtils tun. Es macht keinen Sinn, in die andere Richtung zu gehen ... hoffentlich macht das Sinn

AKTUALISIEREN:

Je mehr ich darüber nachdenke, desto mehr kann ich natürlich sehen, wie dies tatsächlich erforderlich wäre. Ich kenne einige der Kommentare, in denen PipedEingabe- / Ausgabestreams erwähnt wurden, aber es gibt eine andere Möglichkeit.

Wenn der exponierte Ausgabestream a ist ByteArrayOutputStream, können Sie jederzeit den vollständigen Inhalt abrufen, indem Sie die toByteArray()Methode aufrufen . Anschließend können Sie mithilfe der ByteArrayInputStreamUnterklasse einen Eingabestream-Wrapper erstellen . Diese beiden sind Pseudo-Streams, beide umschließen im Grunde nur ein Array von Bytes. Die Verwendung der Streams auf diese Weise ist daher technisch möglich, aber für mich ist es immer noch sehr seltsam ...

Java Drinker
quelle
4
copy () mache dies IS to OS gemäß API, ich brauche es rückwärts zu tun
Waypoint
1
Siehe meine Bearbeitung oben, es ist notwendig für mich, eine Konvertierung vorzunehmen
Wegpunkt
86
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 verwendet. Sie müssen also den OutputStream von JSON über eine HTTP-Verbindung leiten, die von einem InputStream lesen möchte.
JBCP
6
Dies ist nützlich beim Testen von Einheiten und Sie sind sehr umständlich, wenn Sie vermeiden, das Dateisystem zu berühren.
Jon
26
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 zum Speichern eines PDF-Objekts, und die REST-API akzeptiert einen InputStream, um dem Client zu antworten. Daher ist ein OutputStream -> InputStream ein sehr realer Anwendungsfall.
John Manko
200

Es scheint viele Links und ähnliches zu geben, aber keinen tatsächlichen Code, der Pipes verwendet. Der Vorteil von java.io.PipedInputStreamund java.io.PipedOutputStreambesteht darin, dass kein zusätzlicher Speicherverbrauch anfällt. ByteArrayOutputStream.toByteArray()Gibt eine Kopie des ursprünglichen Puffers zurück. Das bedeutet, dass Sie jetzt zwei Kopien davon haben, was auch immer Sie im Speicher haben. Wenn InputStreamSie dann auf ein schreiben, haben Sie jetzt drei Kopien der Daten.

Der Code:

// 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 setzt voraus, dass originalByteArrayOutputStreames sich um einen handelt, ByteArrayOutputStreamda dies normalerweise der einzige verwendbare Ausgabestream ist, es sei denn, Sie schreiben in eine Datei. Ich hoffe das hilft! Das Tolle daran ist, dass es, da es sich in einem separaten Thread befindet, auch parallel funktioniert. Was also Ihren Eingabestream verbraucht, wird auch aus Ihrem alten Ausgabestream gestreamt. Dies ist vorteilhaft, da der Puffer kleiner bleiben kann und Sie weniger Latenz und weniger Speicher benötigen.

mikeho
quelle
21
Ich habe dafür gestimmt, aber es ist besser, outan inden Konstruktor zu übergeben, da Sie sonst inaufgrund der Rennbedingungen (die ich erlebt habe) möglicherweise eine Ausnahme für geschlossene Rohre erhalten . Verwenden von Java 8 Lambdas:PipedInputStream in = new PipedInputStream(out); ((Runnable)() -> {originalOutputStream.writeTo(out);}).run(); return in;
John Manko
1
@ JohnManko hmm ... Ich hatte noch nie dieses Problem. Haben Sie dies erlebt, weil ein anderer Thread oder der Hauptthread out.close () aufruft? Es ist wahr, dass dieser Code davon ausgeht, dass Ihr PipedOutputStream länger hält als Ihr, originalOutputStreamwas wahr sein sollte, aber er setzt nicht voraus, wie Sie Ihre Streams steuern. Das bleibt dem Entwickler überlassen. Dieser Code enthält nichts, was eine Ausnahme für geschlossene oder unterbrochene Rohre verursachen würde.
Mikeho
3
Nein, mein Fall stammt aus dem Speichern von PDFs in Mongo GridFS und dem anschließenden Streaming mit Jax-RS an den Client. MongoDB liefert einen OutputStream, Jax-RS benötigt jedoch einen InputStream. Meine Pfadmethode würde anscheinend mit einem InputStream zum Container zurückkehren, bevor der OutputStream vollständig eingerichtet wurde (möglicherweise wurde der Puffer noch nicht zwischengespeichert). Auf jeden Fall würde Jax-RS eine Pipe-Closed-Ausnahme auf dem InputStream auslösen. Seltsam, aber genau das ist die halbe Zeit passiert. Das Ändern des obigen Codes verhindert dies.
John Manko
1
@ JohnManko Ich habe mir das genauer angesehen und aus den PipedInputStreamJavadocs gesehen: Eine Pipe wird als unterbrochen bezeichnet, wenn ein Thread, der Datenbytes für den verbundenen Pipeline-Ausgabestream bereitstellt, nicht mehr aktiv ist. Ich vermute also, dass der Thread abgeschlossen ist, wenn Sie das obige Beispiel verwenden, bevor Jax-RSder Eingabestream verbraucht wird. Gleichzeitig habe ich mir die MongoDB Javadocs angesehen. GridFSDBFilehat einen Eingabestream, warum also nicht einfach an Jax-RS weitergeben ?
Mikeho
3
@ TennisCheung ja natürlich. Nichts ist kostenlos, aber es wird sicherlich kleiner als eine 15-MB-Kopie sein. Zu den Optimierungen gehört die Verwendung eines Thread-Pools, um die GC-Abwanderung bei konstanter Thread- / Objekterstellung zu reduzieren.
Mikeho
40

Da Eingabe- und Ausgabestreams nur Start- und Endpunkt sind, besteht die Lösung darin, Daten vorübergehend in einem Byte-Array zu speichern. Sie müssen also ein Zwischenprodukt erstellen ByteArrayOutputStream, aus dem Sie ein Zwischenprodukt erstellen byte[], das als Eingabe 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.

BorutT
quelle
baos.toByteArray () erstellt eine Kopie mit System.arraycopy. Vielen Dank an @mikeho für den Hinweis auf developer.classpath.org/doc/java/io/…
Mitja Gustin
20

Sie benötigen eine Zwischenklasse, die zwischen puffert. Bei jedem InputStream.read(byte[]...)Aufruf füllt die Pufferklasse das übergebene Byte-Array mit dem nächsten übergebenen Block von OutputStream.write(byte[]...). Da die Größe der Chunks möglicherweise nicht gleich ist, muss die Adapterklasse eine bestimmte Menge speichern, bis sie genug hat, um den Lesepuffer zu füllen und / oder einen Pufferüberlauf speichern zu können.

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

mckamey
quelle
1
danke @mckamey, die auf Circular Buffers basierende Methode ist genau das, was ich brauche!
Hui Wang
18
ByteArrayOutputStream buffer = (ByteArrayOutputStream) aOutputStream;
byte[] bytes = buffer.toByteArray();
InputStream inputStream = new ByteArrayInputStream(bytes);
Opster Elasticsearch Pro-Vijay
quelle
2
Sie sollten dies nicht verwenden, da der toByteArray()Methodenkörper wie folgt return Arrays.copyOf(buf, count);aussieht und ein neues Array zurückgibt.
Wurzel G
9

Ich bin auf das gleiche Problem bei der Konvertierung von a ByteArrayOutputStreamin a gestoßen ByteArrayInputStreamund habe es mithilfe einer abgeleiteten Klasse gelöst, von der ByteArrayOutputStreama zurückgegeben werden kannByteArrayInputStream , die mit dem internen Puffer von initialisiert wurde 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

Nick Russler
quelle
Was ist, wenn der Inhalt nicht in den Puffer passt?
Vadimo
Dann sollten Sie überhaupt keinen ByteArrayInputStream verwenden.
Nick Russler
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
Vadimo
1
Wenn Sie toByteArray meinen, würde dies 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
Zeitverschwendung. ByteArrayOutputStream hat eine writeTo-Methode, um Inhalte in einen anderen Ausgabestream zu übertragen
Tony BenBrahim
3

Die io-Extras der Bibliothek können nützlich sein. Zum Beispiel, wenn Sie eine InputStreamVerwendung GZIPOutputStreamgzipen möchten und möchten, dass sie synchron erfolgt (unter Verwendung der Standardpuffergröße von 8192):

InputStream is = ...
InputStream gz = IOUtil.pipe(is, o -> new GZIPOutputStream(o));

Beachten Sie, dass die Bibliothek zu 100% über Unit-Tests verfügt (was das natürlich wert ist!) Und sich in Maven Central befindet. Die Maven-Abhängigkeit ist:

<dependency>
  <groupId>com.github.davidmoten</groupId>
  <artifactId>io-extras</artifactId>
  <version>0.1</version>
</dependency>

Achten Sie darauf, nach einer späteren Version zu suchen.

Dave Moten
quelle
0

Aus meiner Sicht ist java.io.PipedInputStream / java.io.PipedOutputStream die beste Option. In einigen Situationen möchten Sie möglicherweise ByteArrayInputStream / ByteArrayOutputStream verwenden. Das Problem ist, dass Sie den Puffer duplizieren müssen, um einen ByteArrayOutputStream in einen ByteArrayInputStream zu konvertieren. Auch ByteArrayOutpuStream / ByteArrayInputStream sind auf 2 GB beschränkt. Hier ist eine OutpuStream / InputStream-Implementierung, die ich geschrieben habe, um die Einschränkungen von ByteArrayOutputStream / ByteArrayInputStream zu umgehen (Scala-Code, aber für Java-Entwickler leicht verständlich):

import java.io.{IOException, InputStream, OutputStream}

import scala.annotation.tailrec

/** Acts as a replacement for ByteArrayOutputStream
  *
  */
class HugeMemoryOutputStream(capacity: Long) extends OutputStream {
  private val PAGE_SIZE: Int = 1024000
  private val ALLOC_STEP: Int = 1024

  /** Pages array
    *
    */
  private var streamBuffers: Array[Array[Byte]] = Array.empty[Array[Byte]]

  /** Allocated pages count
    *
    */
  private var pageCount: Int = 0

  /** Allocated bytes count
    *
    */
  private var allocatedBytes: Long = 0

  /** Current position in stream
    *
    */
  private var position: Long = 0

  /** Stream length
    *
    */
  private var length: Long = 0

  allocSpaceIfNeeded(capacity)

  /** Gets page count based on given length
    *
    * @param length   Buffer length
    * @return         Page count to hold the specified amount of data
    */
  private def getPageCount(length: Long) = {
    var pageCount = (length / PAGE_SIZE).toInt + 1

    if ((length % PAGE_SIZE) == 0) {
      pageCount -= 1
    }

    pageCount
  }

  /** Extends pages array
    *
    */
  private def extendPages(): Unit = {
    if (streamBuffers.isEmpty) {
      streamBuffers = new Array[Array[Byte]](ALLOC_STEP)
    }
    else {
      val newStreamBuffers = new Array[Array[Byte]](streamBuffers.length + ALLOC_STEP)
      Array.copy(streamBuffers, 0, newStreamBuffers, 0, streamBuffers.length)
      streamBuffers = newStreamBuffers
    }

    pageCount = streamBuffers.length
  }

  /** Ensures buffers are bug enough to hold specified amount of data
    *
    * @param value  Amount of data
    */
  private def allocSpaceIfNeeded(value: Long): Unit = {
    @tailrec
    def allocSpaceIfNeededIter(value: Long): Unit = {
      val currentPageCount = getPageCount(allocatedBytes)
      val neededPageCount = getPageCount(value)

      if (currentPageCount < neededPageCount) {
        if (currentPageCount == pageCount) extendPages()

        streamBuffers(currentPageCount) = new Array[Byte](PAGE_SIZE)
        allocatedBytes = (currentPageCount + 1).toLong * PAGE_SIZE

        allocSpaceIfNeededIter(value)
      }
    }

    if (value < 0) throw new Error("AllocSpaceIfNeeded < 0")
    if (value > 0) {
      allocSpaceIfNeededIter(value)

      length = Math.max(value, length)
      if (position > length) position = length
    }
  }

  /**
    * Writes the specified byte to this output stream. The general
    * contract for <code>write</code> is that one byte is written
    * to the output stream. The byte to be written is the eight
    * low-order bits of the argument <code>b</code>. The 24
    * high-order bits of <code>b</code> are ignored.
    * <p>
    * Subclasses of <code>OutputStream</code> must provide an
    * implementation for this method.
    *
    * @param      b the <code>byte</code>.
    */
  @throws[IOException]
  override def write(b: Int): Unit = {
    val buffer: Array[Byte] = new Array[Byte](1)

    buffer(0) = b.toByte

    write(buffer)
  }

  /**
    * Writes <code>len</code> bytes from the specified byte array
    * starting at offset <code>off</code> to this output stream.
    * The general contract for <code>write(b, off, len)</code> is that
    * some of the bytes in the array <code>b</code> are written to the
    * output stream in order; element <code>b[off]</code> is the first
    * byte written and <code>b[off+len-1]</code> is the last byte written
    * by this operation.
    * <p>
    * The <code>write</code> method of <code>OutputStream</code> calls
    * the write method of one argument on each of the bytes to be
    * written out. Subclasses are encouraged to override this method and
    * provide a more efficient implementation.
    * <p>
    * If <code>b</code> is <code>null</code>, a
    * <code>NullPointerException</code> is thrown.
    * <p>
    * If <code>off</code> is negative, or <code>len</code> is negative, or
    * <code>off+len</code> is greater than the length of the array
    * <code>b</code>, then an <tt>IndexOutOfBoundsException</tt> is thrown.
    *
    * @param      b   the data.
    * @param      off the start offset in the data.
    * @param      len the number of bytes to write.
    */
  @throws[IOException]
  override def write(b: Array[Byte], off: Int, len: Int): Unit = {
    @tailrec
    def writeIter(b: Array[Byte], off: Int, len: Int): Unit = {
      val currentPage: Int = (position / PAGE_SIZE).toInt
      val currentOffset: Int = (position % PAGE_SIZE).toInt

      if (len != 0) {
        val currentLength: Int = Math.min(PAGE_SIZE - currentOffset, len)
        Array.copy(b, off, streamBuffers(currentPage), currentOffset, currentLength)

        position += currentLength

        writeIter(b, off + currentLength, len - currentLength)
      }
    }

    allocSpaceIfNeeded(position + len)
    writeIter(b, off, len)
  }

  /** Gets an InputStream that points to HugeMemoryOutputStream buffer
    *
    * @return InputStream
    */
  def asInputStream(): InputStream = {
    new HugeMemoryInputStream(streamBuffers, length)
  }

  private class HugeMemoryInputStream(streamBuffers: Array[Array[Byte]], val length: Long) extends InputStream {
    /** Current position in stream
      *
      */
    private var position: Long = 0

    /**
      * Reads the next byte of data from the input stream. The value byte is
      * returned as an <code>int</code> in the range <code>0</code> to
      * <code>255</code>. If no byte is available because the end of the stream
      * has been reached, the value <code>-1</code> is returned. This method
      * blocks until input data is available, the end of the stream is detected,
      * or an exception is thrown.
      *
      * <p> A subclass must provide an implementation of this method.
      *
      * @return the next byte of data, or <code>-1</code> if the end of the
      *         stream is reached.
      */
    @throws[IOException]
    def read: Int = {
      val buffer: Array[Byte] = new Array[Byte](1)

      if (read(buffer) == 0) throw new Error("End of stream")
      else buffer(0)
    }

    /**
      * Reads up to <code>len</code> bytes of data from the input stream into
      * an array of bytes.  An attempt is made to read as many as
      * <code>len</code> bytes, but a smaller number may be read.
      * The number of bytes actually read is returned as an integer.
      *
      * <p> This method blocks until input data is available, end of file is
      * detected, or an exception is thrown.
      *
      * <p> If <code>len</code> is zero, then no bytes are read and
      * <code>0</code> is returned; otherwise, there is an attempt to read at
      * least one byte. If no byte is available because the stream is at end of
      * file, the value <code>-1</code> is returned; otherwise, at least one
      * byte is read and stored into <code>b</code>.
      *
      * <p> The first byte read is stored into element <code>b[off]</code>, the
      * next one into <code>b[off+1]</code>, and so on. The number of bytes read
      * is, at most, equal to <code>len</code>. Let <i>k</i> be the number of
      * bytes actually read; these bytes will be stored in elements
      * <code>b[off]</code> through <code>b[off+</code><i>k</i><code>-1]</code>,
      * leaving elements <code>b[off+</code><i>k</i><code>]</code> through
      * <code>b[off+len-1]</code> unaffected.
      *
      * <p> In every case, elements <code>b[0]</code> through
      * <code>b[off]</code> and elements <code>b[off+len]</code> through
      * <code>b[b.length-1]</code> are unaffected.
      *
      * <p> The <code>read(b,</code> <code>off,</code> <code>len)</code> method
      * for class <code>InputStream</code> simply calls the method
      * <code>read()</code> repeatedly. If the first such call results in an
      * <code>IOException</code>, that exception is returned from the call to
      * the <code>read(b,</code> <code>off,</code> <code>len)</code> method.  If
      * any subsequent call to <code>read()</code> results in a
      * <code>IOException</code>, the exception is caught and treated as if it
      * were end of file; the bytes read up to that point are stored into
      * <code>b</code> and the number of bytes read before the exception
      * occurred is returned. The default implementation of this method blocks
      * until the requested amount of input data <code>len</code> has been read,
      * end of file is detected, or an exception is thrown. Subclasses are encouraged
      * to provide a more efficient implementation of this method.
      *
      * @param      b   the buffer into which the data is read.
      * @param      off the start offset in array <code>b</code>
      *                 at which the data is written.
      * @param      len the maximum number of bytes to read.
      * @return the total number of bytes read into the buffer, or
      *         <code>-1</code> if there is no more data because the end of
      *         the stream has been reached.
      * @see java.io.InputStream#read()
      */
    @throws[IOException]
    override def read(b: Array[Byte], off: Int, len: Int): Int = {
      @tailrec
      def readIter(acc: Int, b: Array[Byte], off: Int, len: Int): Int = {
        val currentPage: Int = (position / PAGE_SIZE).toInt
        val currentOffset: Int = (position % PAGE_SIZE).toInt

        val count: Int = Math.min(len, length - position).toInt

        if (count == 0 || position >= length) acc
        else {
          val currentLength = Math.min(PAGE_SIZE - currentOffset, count)
          Array.copy(streamBuffers(currentPage), currentOffset, b, off, currentLength)

          position += currentLength

          readIter(acc + currentLength, b, off + currentLength, len - currentLength)
        }
      }

      readIter(0, b, off, len)
    }

    /**
      * Skips over and discards <code>n</code> bytes of data from this input
      * stream. The <code>skip</code> method may, for a variety of reasons, end
      * up skipping over some smaller number of bytes, possibly <code>0</code>.
      * This may result from any of a number of conditions; reaching end of file
      * before <code>n</code> bytes have been skipped is only one possibility.
      * The actual number of bytes skipped is returned. If <code>n</code> is
      * negative, the <code>skip</code> method for class <code>InputStream</code> always
      * returns 0, and no bytes are skipped. Subclasses may handle the negative
      * value differently.
      *
      * The <code>skip</code> method of this class creates a
      * byte array and then repeatedly reads into it until <code>n</code> bytes
      * have been read or the end of the stream has been reached. Subclasses are
      * encouraged to provide a more efficient implementation of this method.
      * For instance, the implementation may depend on the ability to seek.
      *
      * @param      n the number of bytes to be skipped.
      * @return the actual number of bytes skipped.
      */
    @throws[IOException]
    override def skip(n: Long): Long = {
      if (n < 0) 0
      else {
        position = Math.min(position + n, length)
        length - position
      }
    }
  }
}

Einfach zu bedienen, keine Pufferduplizierung, keine Speicherbeschränkung von 2 GB

val out: HugeMemoryOutputStream = new HugeMemoryOutputStream(initialCapacity /*may be 0*/)

out.write(...)
...

val in1: InputStream = out.asInputStream()

in1.read(...)
...

val in2: InputStream = out.asInputStream()

in2.read(...)
...
Luc Vaillant
quelle
-1

Wenn Sie einen OutputStream aus einem InputStream erstellen möchten, gibt es ein grundlegendes Problem. Eine Methode, die in einen OutputStream schreibt, blockiert, bis dies abgeschlossen ist. Das Ergebnis ist also verfügbar, wenn die Schreibmethode abgeschlossen ist. Dies hat zwei Konsequenzen:

  1. Wenn Sie nur einen Thread verwenden, müssen Sie warten, bis alles geschrieben ist (also müssen Sie die Daten des Streams im Speicher oder auf der Festplatte speichern).
  2. Wenn Sie auf die Daten zugreifen möchten, bevor sie fertig sind, benötigen Sie einen zweiten Thread.

Variante 1 kann mit Byte-Arrays implementiert oder abgelegt werden. Variante 1 kann mithilfe von Pipies implementiert werden (entweder direkt oder mit zusätzlicher Abstraktion - z. B. RingBuffer oder die Google-Bibliothek aus dem anderen Kommentar).

In der Tat gibt es mit Standard-Java keinen anderen Weg, um das Problem zu lösen. Jede Lösung ist eine Implementierung einer dieser Lösungen.

Es gibt ein Konzept namens "Fortsetzung" (siehe Wikipedia für Details). In diesem Fall bedeutet dies im Grunde:

  • Es gibt einen speziellen Ausgabestream, der eine bestimmte Datenmenge erwartet
  • Wenn der Betrag erreicht ist, gibt der Stream seinem Gegenstück, das ein spezieller Eingabestream ist, die Kontrolle
  • Der Eingabestream stellt die Datenmenge zur Verfügung, bis sie gelesen wird. Danach gibt er die Steuerung an den Ausgabestream zurück

Während in einigen Sprachen dieses Konzept integriert ist, benötigen Sie für Java etwas "Magie". Zum Beispiel implementiert "commons-javaflow" von Apache solche für Java. Der Nachteil ist, dass dies einige spezielle Bytecode-Änderungen zur Erstellungszeit erfordert. Es wäre also sinnvoll, das gesamte Material mit benutzerdefinierten Build-Skripten in eine zusätzliche Bibliothek zu stellen.

Michael Wyraz
quelle
-1

Alter Beitrag, könnte aber anderen helfen. Verwenden Sie diesen Weg:

OutputStream out = new ByteArrayOutputStream();
...
out.write();
...
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(out.toString().getBytes()));
Manu
quelle
1
zu String -> Größenproblem
user1594895
Außerdem wird beim Aufrufen toString().getBytes()eines Streams * der Inhalt des Streams nicht zurückgegeben.
Maarten Bodewes
-1

Obwohl Sie einen OutputStream nicht in einen InputStream konvertieren können, bietet Java mithilfe von PipedOutputStream und PipedInputStream eine Möglichkeit, Daten in einen PipedOutputStream schreiben zu lassen, um über einen zugeordneten PipedInputStream verfügbar zu werden.
Vor einiger Zeit sah ich mich einer ähnlichen Situation gegenüber, als ich mich mit Bibliotheken von Drittanbietern befasste, bei denen anstelle einer OutputStream-Instanz eine InputStream-Instanz an diese übergeben werden musste.
Ich habe dieses Problem behoben, indem ich PipedInputStream und PipedOutputStream verwendet habe.
Übrigens sind sie schwierig zu bedienen und Sie müssen Multithreading verwenden, um das zu erreichen, was Sie wollen. Ich habe kürzlich eine Implementierung auf Github veröffentlicht, die Sie verwenden können.
Hier ist der Link . Sie können das Wiki durchgehen, um zu verstehen, wie man es benutzt.

Ranjit Aneesh
quelle