Come convertire OutputStream in InputStream?


337

Sono in fase di sviluppo, dove ho due moduli e da uno ho ottenuto l'output come un OutputStreamsecondo, che accetta solo InputStream. Sapete come convertire OutputStreama InputStream(non viceversa, intendo realmente in questo modo) che sarò in grado di collegare queste due parti?

Grazie



3
@ c0mrade, l'op vuole qualcosa come IOUtils.copy, solo nella direzione opposta. Quando qualcuno scrive in un OutputStream, diventa disponibile per qualcun altro da utilizzare in un InputStream. Questo è fondamentalmente ciò che fanno PipedOutputStream / PipedInputStream. Sfortunatamente i flussi Piped non possono essere creati da altri flussi.
MeBigFatGuy

quindi PipedOutputStream / PipedInputStream è la soluzione?
Waypoint,

Fondamentalmente affinché PipedStreams funzioni nel tuo caso, OutputStream dovrebbe essere costruito come new YourOutputStream(thePipedOutputStream)e new YourInputStream(thePipedInputStream)che probabilmente non è il modo in cui funziona il tuo stream. Quindi non penso che questa sia la soluzione.
MeBigFatGuy

Risposte:


109

Uno OutputStreamè quello in cui scrivi i dati. Se un modulo espone un OutputStream, l'aspettativa è che ci sia qualcosa che legge all'altra estremità.

Qualcosa che espone InputStream, d'altra parte, sta indicando che dovrai ascoltare questo flusso e ci saranno dati che puoi leggere.

Quindi è possibile connettere un InputStreamad unOutputStream

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

Come qualcuno ha accennato, questo è ciò che consente il copy()metodo da IOUtils . Non ha senso andare dall'altra parte ... speriamo che abbia un senso

AGGIORNARE:

Naturalmente più ci penso, più vedo come questo sarebbe effettivamente un requisito. Conosco alcuni dei commenti menzionati Pipedflussi di input / output, ma c'è un'altra possibilità.

Se il flusso di output esposto è a ByteArrayOutputStream, è possibile ottenere sempre il contenuto completo chiamando il toByteArray()metodo Quindi è possibile creare un wrapper del flusso di input utilizzando la ByteArrayInputStreamsottoclasse. Questi due sono pseudo-stream, entrambi sostanzialmente avvolgono una matrice di byte. L'uso dei flussi in questo modo, quindi, è tecnicamente possibile, ma per me è ancora molto strano ...


4
copia () fai questo IS sul SO secondo l'API, ne ho bisogno per fare all'indietro
Waypoint

1
Vedi la mia modifica in alto, è necessario per me fare qualche conversione
Waypoint

88
Il caso d'uso è molto semplice: immagina di avere una libreria di serializzazione (ad esempio, serializzazione su JSON) e un livello di trasporto (diciamo, Tomcat) che accetta un InputStream. Quindi è necessario reindirizzare OutputStream da JSON su una connessione HTTP che vuole leggere da un InputStream.
JBCP,

6
Ciò è utile quando si esegue il test dell'unità e si è super pedanti nell'evitare di toccare il file system.
Jon

28
Il commento di @JBCP è perfetto. Un altro caso d'uso sta usando PDFBox per creare PDF durante una richiesta HTTP. PDFBox utilizza OutputStream per salvare un oggetto PDF e l'API REST accetta InputStream per rispondere al client. Quindi, un OutputStream -> InputStream è un caso d'uso molto reale.
John Manko,

200

Sembra che ci siano molti collegamenti e altre cose simili, ma nessun codice reale che utilizza le pipe. Il vantaggio di usare java.io.PipedInputStreamed java.io.PipedOutputStreamè che non vi è alcun consumo aggiuntivo di memoria. ByteArrayOutputStream.toByteArray()restituisce una copia del buffer originale, quindi ciò significa che qualunque cosa tu abbia in memoria, ora ne hai due copie. Quindi scrivendo su un InputStreammezzo ora hai tre copie dei dati.

Il codice:

// 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();

Questo codice presuppone che originalByteArrayOutputStreamsia un ByteArrayOutputStreamdato che di solito è l'unico flusso di output utilizzabile, a meno che non si stia scrivendo in un file. Spero che aiuti! La cosa grandiosa di questo è che dal momento che si trova in un thread separato, funziona anche in parallelo, quindi qualunque cosa stia consumando il tuo flusso di input verrà trasmesso anche dal tuo vecchio flusso di output. Questo è utile perché il buffer può rimanere più piccolo e avrai meno latenza e meno utilizzo della memoria.


21
Ho votato a favore, ma è meglio passare outal incostruttore, altrimenti potresti ricevere un'eccezione di pipe chiusa a incausa delle condizioni di gara (che ho riscontrato). Utilizzando Java 8 Lambdas:PipedInputStream in = new PipedInputStream(out); ((Runnable)() -> {originalOutputStream.writeTo(out);}).run(); return in;
John Manko,

1
@JohnManko hmm ... Non ho mai avuto quel problema. Hai riscontrato questo perché un altro thread o il thread principale sta chiamando out.close ()? È vero che questo codice presuppone che PipedOutputStream abbia una vita più lunga di quella originalOutputStreamche dovrebbe essere vera, ma non presuppone come controlli i tuoi flussi. Questo è lasciato allo sviluppatore. Non c'è nulla in questo codice che provocherebbe un'eccezione pipe chiusa o rotta.
mikeho,

3
No, il mio caso deriva da quando memorizzo i PDF in Mongo GridFS e quindi lo streaming sul client utilizzando Jax-RS. MongoDB fornisce un OutputStream, ma Jax-RS richiede un InputStream. Il mio metodo path sarebbe tornato al contenitore con un InputStream prima che OutputStream fosse completamente stabilito, a quanto pare (forse il buffer non era ancora stato memorizzato nella cache). Ad ogni modo, Jax-RS lancerebbe un'eccezione di pipe chiusa su InputStream. Strano, ma è quello che è successo metà del tempo. La modifica al codice sopra impedisce questo.
John Manko,

1
@JohnManko Lo stavo esaminando di più e ho visto dai PipedInputStreamJavadocs: si dice che una pipe sia rotta se un thread che stava fornendo byte di dati al flusso di output di piping collegato non è più attivo. Quindi quello che sospetto è che se stai usando l'esempio sopra, il thread sta completando prima che Jax-RSsta consumando il flusso di input. Allo stesso tempo, ho guardato il Javadocs MongoDB . GridFSDBFileha un flusso di input, quindi perché non trasferirlo a Jax-RS ?
mikeho,

3
@DennisCheung sì, certo. Niente è gratuito, ma sarà sicuramente più piccolo di una copia da 15 MB. Le ottimizzazioni includerebbero invece l'uso di un pool di thread per ridurre il churn GC con creazione costante di thread / oggetti.
mikeho,

40

Poiché i flussi di input e output sono solo punto iniziale e finale, la soluzione è archiviare temporaneamente i dati in array di byte. Quindi è necessario creare intermedio ByteArrayOutputStream, da cui si crea byte[]che viene utilizzato come input per il nuovo 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);
}

Spero che sia d'aiuto.


baos.toByteArray () crea una copia con System.arraycopy. Grazie a @mikeho per aver segnalato developer.classpath.org/doc/java/io/…
Mitja Gustin il

20

Avrai bisogno di una classe intermedia con buffer tra. Ogni volta che InputStream.read(byte[]...)viene chiamato, la classe di buffering riempirà l'array di byte passati con il blocco successivo passato da OutputStream.write(byte[]...). Poiché le dimensioni dei blocchi potrebbero non essere le stesse, la classe dell'adattatore dovrà archiviare un determinato importo fino a quando non avrà abbastanza per riempire il buffer di lettura e / o essere in grado di archiviare qualsiasi overflow del buffer.

Questo articolo presenta una bella suddivisione di alcuni approcci diversi a questo problema:

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


1
grazie @mckamey, il metodo basato sui buffer circolari è esattamente quello di cui ho bisogno!
Hui Wang

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

2
Non si dovrebbe usare questo poiché il toByteArray()metodo body è come questo return Arrays.copyOf(buf, count);che restituisce un nuovo array.
Radice G


9

Ho riscontrato lo stesso problema con la conversione di a ByteArrayOutputStreamin ByteArrayInputStreame l'ho risolto utilizzando una classe derivata dalla ByteArrayOutputStreamquale è in grado di restituire un ByteArrayInputStreaminizializzato con il buffer interno di ByteArrayOutputStream. In questo modo non viene utilizzata memoria aggiuntiva e la 'conversione' è molto veloce:

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;
    }
}

Ho messo le cose su github: https://github.com/nickrussler/ByteArrayInOutStream


cosa succede se il contenuto non si adatta al buffer?
Vadimo,

Quindi non dovresti usare un ByteArrayInputStream in primo luogo.
Nick Russler,

Questa soluzione avrà tutti i byte in memoria. Per file di piccole dimensioni questo andrà bene, ma puoi anche usare getBytes () su ByteArrayOutput Stream
Vadimo

1
Se intendiByteArray questo causerebbe la copia del buffer interno, che richiederebbe il doppio della memoria del mio approccio. Modifica: Ah, capisco, per file di piccole dimensioni questo funziona ovviamente ..
Nick Russler,

Perdita di tempo. ByteArrayOutputStream ha un metodo writeTo per trasferire il contenuto su un altro flusso di output
Tony BenBrahim,

3

La libreria io-extra può essere utile. Ad esempio, se si desidera gzip un InputStreamutilizzo GZIPOutputStreame si desidera che avvenga in modo sincrono (utilizzando la dimensione del buffer predefinita di 8192):

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

Nota che la libreria ha una copertura del 100% per i test unitari (per quello che vale ovviamente!) Ed è su Maven Central. La dipendenza Maven è:

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

Assicurati di verificare una versione successiva.


0

Dal mio punto di vista, java.io.PipedInputStream / java.io.PipedOutputStream è l'opzione migliore da considerare. In alcune situazioni potresti voler usare ByteArrayInputStream / ByteArrayOutputStream. Il problema è che è necessario duplicare il buffer per convertire un ByteArrayOutputStream in un ByteArrayInputStream. Anche ByteArrayOutpuStream / ByteArrayInputStream sono limitati a 2 GB. Ecco un'implementazione OutpuStream / InputStream che ho scritto per bypassare le limitazioni di ByteArrayOutputStream / ByteArrayInputStream (codice Scala, ma facilmente comprensibile per gli sviluppatori Java):

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
      }
    }
  }
}

Facile da usare, nessuna duplicazione del buffer, nessun limite di memoria di 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(...)
...

-1

Se si desidera creare un OutputStream da un InputStream, esiste un problema di base. Un metodo che scrive su un OutputStream blocca fino a quando non viene eseguito. Quindi il risultato è disponibile al termine del metodo di scrittura. Ciò ha 2 conseguenze:

  1. Se si utilizza solo un thread, è necessario attendere fino a quando non viene scritto tutto (quindi è necessario archiviare i dati del flusso in memoria o su disco).
  2. Se si desidera accedere ai dati prima che siano finiti, è necessario un secondo thread.

La variante 1 può essere implementata usando array di byte o archiviata. La variante 1 può essere implementata usando pipies (direttamente o con ulteriore astrazione - ad esempio RingBuffer o la google lib dell'altro commento).

In effetti con Java standard non c'è altro modo per risolvere il problema. Ogni soluzione è un'implementazione di uno di questi.

C'è un concetto chiamato "continuazione" (vedi Wikipedia per i dettagli). In questo caso in pratica ciò significa:

  • esiste un flusso di output speciale che prevede una determinata quantità di dati
  • se viene raggiunta la quantità, il flusso dà il controllo alla sua controparte che è un flusso di input speciale
  • il flusso di input rende disponibile la quantità di dati fino a quando non viene letto, dopodiché restituisce il controllo al flusso di output

Mentre alcune lingue hanno questo concetto integrato, per Java hai bisogno di un po 'di "magia". Ad esempio "commons-javaflow" di apache implementa tale per java. Lo svantaggio è che questo richiede alcune speciali modifiche al bytecode al momento della creazione. Quindi avrebbe senso mettere tutto in una libreria extra con script di build personalizzati.


-1

Vecchio post ma potrebbe aiutare gli altri, utilizzare in questo modo:

OutputStream out = new ByteArrayOutputStream();
...
out.write();
...
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(out.toString().getBytes()));

1
alla stringa -> problema di dimensione
user1594895

Inoltre, la chiamata toString().getBytes()a uno stream * non restituirà il contenuto dello stream.
Maarten Bodewes,

-1

Sebbene non sia possibile convertire un OutputStream in un InputStream, java fornisce un modo utilizzando PipedOutputStream e PipedInputStream che è possibile disporre dei dati scritti su PipedOutputStream per renderli disponibili tramite un PipedInputStream associato.
Qualche tempo fa ho affrontato una situazione simile quando ho a che fare con librerie di terze parti che richiedevano il passaggio di un'istanza InputStream anziché un'istanza OutputStream.
Il modo in cui ho risolto questo problema è utilizzare PipedInputStream e PipedOutputStream.
A proposito, sono difficili da usare e devi usare il multithreading per ottenere ciò che desideri. Di recente ho pubblicato un'implementazione su github che puoi usare.
Ecco il Link . Puoi consultare il wiki per capire come usarlo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.