Як конвертувати OutputStream у InputStream?


337

Я перебуваю на стадії розробки, де у мене є два модулі, і з одного я отримав вихід у якості OutputStreamдругого, який приймає тільки InputStream. Ви знаєте , як конвертувати OutputStreamв InputStream( а не навпаки, я маю в виду на насправді так) , що я буду мати можливість з'єднати ці дві частини?

Дякую



3
@ c0mrade, оператор хоче щось подібне до IOUtils.copy, лише в іншому напрямку. Коли хтось пише в OutputStream, він стає доступним для використання іншим в InputStream. Це в основному те, що роблять PipedOutputStream / PipedInputStream. На жаль, трубопроводи не можуть бути побудовані з інших потоків.
MeBigFatGuy

значить, PipedOutputStream / PipedInputStream - це рішення?
Waypoint

В основному для того, щоб PipedStreams працював у вашому випадку, ваш OutputStream повинен бути сконструйований як, new YourOutputStream(thePipedOutputStream)і new YourInputStream(thePipedInputStream)це, мабуть, не так, як працює ваш потік. Тому я не думаю, що це рішення.
MeBigFatGuy

Відповіді:


109

Ан OutputStream- це те, куди ви записуєте дані. Якщо якийсь модуль виставляє антену OutputStream, очікується, що на іншому кінці є щось читання.

Щось, що викриває InputStream, з іншого боку, означає, що вам потрібно буде прослухати цей потік, і з’являться дані, які ви зможете прочитати.

Таким чином, можливо підключити InputStreamаOutputStream

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

Як хтось зазначав, це те, що дозволяє вам використовувати copy()метод IOUtils . Немає сенсу йти іншим шляхом ... сподіваємось, це має певний сенс

ОНОВЛЕННЯ:

Звичайно, чим більше я думаю про це, тим більше я бачу, як це насправді було б вимогою. Я знаю деякі коментарі згаданих Pipedпотоків введення / виведення, але є й інша можливість.

Якщо вихідний потік, який піддається впливу, є a ByteArrayOutputStream, ви завжди можете отримати повний вміст, викликавши toByteArray()метод. Потім ви можете створити обгортку вхідного потоку, використовуючи ByteArrayInputStreamпідклас. Ці два - псевдопотоки, вони обидва в основному просто обертають масив байтів. Таким чином, використовувати потоки таким чином технічно можливо, але мені це все ще дуже дивно ...


4
Скопіюйте () зробіть це в ОС відповідно до API, мені потрібно це зробити назад
Waypoint

1
Перегляньте мою редакцію вгорі, мені потрібно здійснити певну конверсію
Waypoint

88
Шкаф використання дуже простий: Уявіть, що у вас є бібліотека серіалізації (наприклад, серіалізація в JSON) та транспортний шар (скажімо, Tomcat), який приймає InputStream. Тому вам потрібно передавати OutputStream від JSON через HTTP-з'єднання, яке хоче читати з InputStream.
JBCP

6
Це корисно при тестуванні одиниць, і ви з великим педантичністю уникаєте торкатися файлової системи.
Джон

28
Коментар @JBCP - це місце. Іншим випадком використання є використання PDFBox для створення PDF-файлів під час HTTP-запиту. PDFBox використовує OutputStream для збереження об'єкта PDF, а API REST приймає InputStream для відповіді клієнту. Отже, OutputStream -> InputStream - це дуже реальний варіант використання.
Джон Манько

200

Здається, існує багато посилань та інших подібних матеріалів, але фактичного коду з використанням труб не існує. Перевага використання java.io.PipedInputStreamта java.io.PipedOutputStreamполягає в тому, що немає додаткового споживання пам’яті. ByteArrayOutputStream.toByteArray()повертає копію оригінального буфера, так що це означає, що все, що у вас є в пам'яті, тепер у вас є дві копії. Тоді пишіть до InputStreamзасобу, тепер у вас є три копії даних.

Код:

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

Цей код передбачає, що originalByteArrayOutputStreamце а, ByteArrayOutputStreamяк правило, єдиний корисний вихідний потік, якщо ви не пишете у файл. Я сподіваюся, що це допомагає! Чудова річ у тому, що оскільки він знаходиться в окремому потоці, він також працює паралельно, тому все, що споживає ваш вхідний потік, буде витікати і зі старого вихідного потоку. Це вигідно, тому що буфер може залишатися меншим, і у вас буде менше затримок і менше використання пам'яті.


21
Я проголосував за це, але краще перейти outдо inконструктора 's, інакше у вас може виникнути виняток із закритого каналу inчерез стан перегонів (який я відчував). Використання Java 8 Lambdas:PipedInputStream in = new PipedInputStream(out); ((Runnable)() -> {originalOutputStream.writeTo(out);}).run(); return in;
Джон Манко

1
@JohnManko Хм ... я ніколи не мав такої проблеми. Чи виникали це у вас через те, що інший або основний потік викликає out.close ()? Це правда, що цей код передбачає, що ваш PipedOutputStream триваліший, ніж ваш, originalOutputStreamщо має бути правдою, але він не передбачає, як ви керуєте своїми потоками. Це залишається за розробником. У цьому коді немає нічого, що могло б спричинити виключення закритої або зламаної труби.
mikeho

3
Ні, мій випадок випливає з того, що я зберігаю PDF-файли в Mongo GridFS, а потім передаю клієнту за допомогою Jax-RS. MongoDB постачає OutputStream, але Jax-RS вимагає InputStream. Мій метод шляху повернеться до контейнера з InputStream до того, як OutputStream буде повністю встановлений, здається (можливо, буфер ще не був кешований). У будь-якому разі, Jax-RS кине закритий виняток труби на InputStream. Як це не дивно, але це сталося половину часу. Перехід до коду вище запобігає цьому.
Джон Манько

1
@JohnManko Я розглядав це більше, і я побачив з PipedInputStreamJavadocs: Кажуть, що труба розірвана, якщо нитка, яка надає байти даних до підключеного потокового вихідного потоку, вже не живе. Тому я підозрюю, що якщо ви використовуєте приклад вище, потік завершується до того, як Jax-RSбуде витрачено вхідний потік. У той же час я подивився на MongoDB Javadocs. GridFSDBFileмає вхідний потік, то чому б не просто передати його Jax-RS ?
mikeho

3
@DennisCheung так, звичайно. Нічого не безкоштовно, але, безумовно, він буде меншим, ніж копія 15 Мб. Оптимізація передбачає використання пулу потоків замість того, щоб зменшити відбиття GC при постійному створенні потоку / об'єкта.
mikeho

40

Оскільки потоки введення та виведення є лише початковою та кінцевою точкою, рішення полягає у тимчасовому зберіганні даних у масиві байтів. Отже, ви повинні створити проміжний ByteArrayOutputStream, з якого ви створюєте, byte[]який використовується як вхід для нового 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);
}

Сподіваюся, це допомагає.


baos.toByteArray () створює копію за допомогою System.arraycopy. Дякуємо @mikeho за те, що він вказав developer.classpath.org/doc/java/io/…
Мітя Густін

20

Вам знадобиться проміжний клас, який буде буферувати між. Кожен раз, коли InputStream.read(byte[]...)викликається, клас буферизації буде заповнювати переданий байтовий масив наступним фрагментом, переданим з OutputStream.write(byte[]...). Оскільки розміри шматочків можуть бути не однаковими, клас адаптера повинен буде зберігати певну кількість, поки його не вистачить для заповнення зчитувального буфера та / або зможе зберігати будь-яке переповнення буфера.

У цій статті є зручна розбивка кількох різних підходів до цієї проблеми:

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


1
дякую @mckamey, метод, заснований на кругових буферах, саме те, що мені потрібно!
Хуй Ван

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

2
Не слід використовувати це, оскільки toByteArray()тіло методу подібне до цього, return Arrays.copyOf(buf, count);що повертає новий масив.
Корінь G

17

Easystream бібліотека з відкритим вихідним кодом має пряму підтримку для перетворення OutputStream до InputStream: http://io-tools.sourceforge.net/easystream/tutorial/tutorial.html

Вони також перераховують інші параметри: http://io-tools.sourceforge.net/easystream/OutputStream_to_InputStream.html


1
Так !, використовуйте easystream!
smartwjw

9

Я зіткнувся з тією ж проблемою з перетворенням a ByteArrayOutputStreamв a ByteArrayInputStreamі вирішив його, використовуючи похідний клас, з ByteArrayOutputStreamякого можна повернути a ByteArrayInputStream, ініціалізований внутрішнім буфером ByteArrayOutputStream. Таким чином, додаткова пам'ять не використовується, і "конверсія" відбувається дуже швидко:

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

Я кладу речі на github: https://github.com/nickrussler/ByteArrayInOutStream


що робити, якщо вміст не вкладається в буфер?
Вадимо

Тоді вам не слід в першу чергу використовувати ByteArrayInputStream.
Нік Расслер

Це рішення матиме всі байти в пам'яті. Для невеликих файлів це буде нормально, але тоді ви також можете використовувати getBytes () на потоці
ByteArrayOutput

1
Якщо ви маєте на увазі toByteArray, це призведе до копіювання внутрішнього буфера, що займе вдвічі більше пам’яті, ніж мій підхід. Редагувати: Ну, я розумію, для невеликих файлів це працює звичайно ..
Нік Расселлер

Марна трата часу. ByteArrayOutputStream має метод writeTo для передачі вмісту в інший вихідний потік
Тоні БенБрахім

3

Бібліотечні іо-додатки можуть бути корисними. Наприклад, якщо ви бажаєте отримати gzip з InputStreamвикористанням, GZIPOutputStreamі ви хочете, щоб це відбувалося синхронно (використовуючи розмір буфера за замовчуванням 8192):

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

Зауважте, що бібліотека має 100% тестового покриття (для чого це варто, звичайно!) Та знаходиться на Maven Central. Залежність Мейвена:

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

Обов’язково перевірте більш пізню версію.


0

З моєї точки зору, java.io.PipedInputStream / java.io.PipedOutputStream - найкращий варіант, який слід враховувати. У деяких ситуаціях ви можете використовувати ByteArrayInputStream / ByteArrayOutputStream. Проблема полягає в тому, що вам потрібно дублювати буфер, щоб перетворити ByteArrayOutputStream у ByteArrayInputStream. Також ByteArrayOutpuStream / ByteArrayInputStream обмежені 2 Гб. Ось реалізація OutpuStream / InputStream, яку я написав, щоб обійти обмеження ByteArrayOutputStream / ByteArrayInputStream (код Scala, але легко зрозумілий для розробників 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
      }
    }
  }
}

Проста у використанні, без дублювання буфера, без обмеження на 2 Гб пам'яті

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

Якщо ви хочете зробити OutputStream з InputStream, є одна основна проблема. Метод запису до блоків OutputStream, поки це не буде виконано. Тож результат доступний, коли закінчується метод запису. Це має 2 наслідки:

  1. Якщо ви використовуєте лише один потік, вам потрібно дочекатися, коли все буде записано (значить, вам потрібно зберегти дані потоку в пам'яті або диску).
  2. Якщо ви хочете отримати доступ до даних до її завершення, вам потрібен другий потік.

Варіант 1 може бути реалізований за допомогою байтових масивів або поданий. Варіант 1 може бути реалізований за допомогою піпі (безпосередньо або з додатковою абстракцією - наприклад, RingBuffer або губ google з іншого коментаря).

Дійсно, зі стандартною Java немає іншого способу вирішити проблему. Кожне рішення - це реалізація одного з них.

Існує одне поняття під назвою "продовження" (детальніше див. У Вікіпедії ). В основному це означає:

  • є спеціальний вихідний потік, який очікує певного обсягу даних
  • якщо сума досягається, потік передає контроль своєму аналогу, який є спеціальним вхідним потоком
  • вхідний потік надає кількість даних, доступних до його зчитування, після цього передає керування назад у вихідний потік

Хоча в деяких мовах вбудована ця концепція, для Java вам потрібна якась "магія". Наприклад, "commons-javaflow" від apache реалізує такі, як java. Недоліком є ​​те, що це вимагає деяких спеціальних модифікацій байт-коду під час збирання. Тому було б доцільно розмістити всі речі в додатковій бібліотеці із спеціальними сценаріями побудови.


-1

Стара публікація, але може допомогти іншим. Використовуйте цей спосіб:

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

1
до String -> проблема з розміром
user1594895

Крім того, виклик toString().getBytes()потоку * не поверне вміст потоку.
Maarten Bodewes

-1

Хоча ви не можете перетворити OutputStream в InputStream, java забезпечує спосіб, використовуючи PipedOutputStream та PipedInputStream, що ви можете мати дані, записані в PipedOutputStream, щоб стати доступними через пов'язаний PipedInputStream.
Якось назад я стикався з подібною ситуацією, коли працював із сторонніми бібліотеками, які вимагали передавати їм екземпляр InputStream замість екземпляра OutputStream.
Я вирішив цю проблему - використовувати PipedInputStream та PipedOutputStream.
До речі, вони складні у використанні, і ви повинні використовувати багатопотокові, щоб досягти того, що ви хочете. Нещодавно я опублікував реалізацію на github, яку ви можете використовувати.
Ось посилання . Ви можете пройти вікі, щоб зрозуміти, як ним користуватися.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.