Làm cách nào để chuyển đổi OutputStream thành InputStream?


337

Tôi đang ở giai đoạn phát triển, nơi tôi có hai mô-đun và từ một mô-đun tôi có đầu ra là một OutputStreamvà thứ hai, chỉ chấp nhận InputStream. Bạn có biết làm thế nào để chuyển đổi OutputStreamsang InputStream(không phải ngược lại, ý tôi là thực sự theo cách này) rằng tôi sẽ có thể kết nối hai phần này?

Cảm ơn



3
@ c0mrade, op muốn một cái gì đó như IOUtils.copy, chỉ theo hướng khác. Khi ai đó viết vào OutputStream, nó sẽ có sẵn cho người khác sử dụng trong InputStream. Về cơ bản, đây là những gì PipedOutputStream / PipedInputStream làm. Thật không may, các luồng Piped không thể được xây dựng từ các luồng khác.
MeBigFatGuy

vậy PipedOutputStream / PipedInputStream là giải pháp?
Waypoint

Về cơ bản để PipedStreams hoạt động trong trường hợp của bạn, OutputStream của bạn sẽ cần phải được xây dựng như thế new YourOutputStream(thePipedOutputStream)new YourInputStream(thePipedInputStream)có lẽ đó không phải là cách hoạt động của luồng của bạn. Vì vậy, tôi không nghĩ rằng đây là giải pháp.
MeBigFatGuy

Câu trả lời:


109

An OutputStreamlà một trong những nơi bạn viết dữ liệu. Nếu một số mô-đun phơi bày một OutputStream, kỳ vọng là có một cái gì đó đọc ở đầu kia.

InputStreamMặt khác, một cái gì đó phơi bày một , cho thấy rằng bạn sẽ cần nghe luồng này, và sẽ có dữ liệu mà bạn có thể đọc.

Vì vậy, có thể kết nối InputStreammộtOutputStream

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

Như ai đó đã nói, đây là những gì copy()phương pháp từ IOUtils cho phép bạn làm. Không có nghĩa gì để đi theo cách khác ... hy vọng điều này có ý nghĩa

CẬP NHẬT:

Tất nhiên tôi càng nghĩ về điều này, tôi càng có thể thấy điều này thực sự sẽ là một yêu cầu như thế nào. Tôi biết một số ý kiến ​​đề cập đến Pipedluồng đầu vào / đầu ra, nhưng có một khả năng khác.

Nếu luồng đầu ra được hiển thị là a ByteArrayOutputStream, thì bạn luôn có thể nhận được toàn bộ nội dung bằng cách gọi toByteArray()phương thức. Sau đó, bạn có thể tạo một trình bao bọc luồng đầu vào bằng cách sử dụng ByteArrayInputStreamlớp con. Hai cái này là các luồng giả, cả hai về cơ bản chỉ gói một mảng byte. Do đó, sử dụng các luồng theo cách này là có thể về mặt kỹ thuật, nhưng với tôi nó vẫn còn rất lạ ...


4
sao chép () thực hiện IS này sang HĐH theo API, tôi cần nó thực hiện ngược
Waypoint

1
Xem bản chỉnh sửa của tôi ở trên cùng, đó là cách để tôi thực hiện một số chuyển đổi
Waypoint

86
Usecase rất đơn giản: Hãy tưởng tượng bạn có một thư viện tuần tự hóa (ví dụ: tuần tự hóa thành JSON) và một lớp vận chuyển (giả sử Tomcat) sẽ lấy InputStream. Vì vậy, bạn cần chuyển kết quả đầu ra từ JSON qua kết nối HTTP muốn đọc từ InputStream.
JBCP

6
Điều này rất hữu ích khi kiểm tra đơn vị và bạn cực kỳ khoa trương về việc tránh chạm vào hệ thống tệp.
Jon

26
Nhận xét của @JBCP là tại chỗ. Một trường hợp sử dụng khác là sử dụng PDFBox để xây dựng các tệp PDF trong khi yêu cầu HTTP. PDFBox sử dụng OutputStream để lưu đối tượng PDF và API REST chấp nhận InputStream để trả lời máy khách. Do đó, OutputStream -> InputStream là trường hợp sử dụng rất thực tế.
John Manko

200

Dường như có nhiều liên kết và những thứ khác như vậy, nhưng không có mã thực tế sử dụng đường ống. Ưu điểm của việc sử dụng java.io.PipedInputStreamjava.io.PipedOutputStreamlà không có thêm bộ nhớ tiêu thụ. ByteArrayOutputStream.toByteArray()trả về một bản sao của bộ đệm ban đầu, vì vậy điều đó có nghĩa là bất cứ thứ gì bạn có trong bộ nhớ, giờ đây bạn có hai bản sao của nó. Sau đó viết thư cho một InputStreamphương tiện bây giờ bạn có ba bản sao của dữ liệu.

Mật mã:

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

Mã này giả định rằng đó originalByteArrayOutputStreamlà một ByteArrayOutputStreamvì nó thường là luồng đầu ra duy nhất có thể sử dụng được, trừ khi bạn ghi vào một tệp. Tôi hi vọng cái này giúp được! Điều tuyệt vời ở đây là vì nó nằm trong một luồng riêng biệt, nên nó cũng hoạt động song song, do đó, bất cứ điều gì đang tiêu thụ luồng đầu vào của bạn cũng sẽ được truyền ra khỏi luồng đầu ra cũ của bạn. Điều đó có lợi vì bộ đệm có thể vẫn nhỏ hơn và bạn sẽ có độ trễ ít hơn và sử dụng ít bộ nhớ hơn.


21
Tôi đã bỏ phiếu này, nhưng tốt hơn là chuyển outcho nhà inxây dựng, nếu không bạn có thể có một ngoại lệ đường ống kín indo điều kiện cuộc đua (mà tôi đã trải nghiệm). Sử dụng Java 8 Lambdas:PipedInputStream in = new PipedInputStream(out); ((Runnable)() -> {originalOutputStream.writeTo(out);}).run(); return in;
John Manko

1
@JohnManko hmm ... Tôi chưa bao giờ gặp vấn đề đó. Bạn đã trải nghiệm điều này bởi vì một chủ đề khác hoặc chủ đề chính đang gọi out.close ()? Đúng là mã này giả định rằng PipedOutputStream của bạn tồn tại lâu hơn so với mã của bạn originalOutputStreamlà đúng, nhưng nó không giả định cách bạn kiểm soát luồng của mình. Đó là tùy thuộc vào nhà phát triển. Không có gì trong mã này sẽ gây ra ngoại lệ đường ống bị đóng hoặc vỡ.
mikeho

3
Không, trường hợp của tôi bắt nguồn từ khi tôi lưu trữ các tệp PDF trong Mongo GridFS, sau đó truyền phát đến máy khách bằng Jax-RS. MongoDB cung cấp một OutputStream, nhưng Jax-RS yêu cầu InputStream. Phương thức đường dẫn của tôi sẽ quay trở lại vùng chứa với InputStream trước khi OutputStream được thiết lập hoàn chỉnh, có vẻ như (có lẽ bộ đệm chưa được lưu vào bộ đệm). Dù sao, Jax-RS sẽ ném ngoại lệ đóng ống trên InputStream. Lạ, nhưng đó là những gì đã xảy ra một nửa thời gian. Thay đổi mã ở trên ngăn chặn điều đó.
John Manko

1
@JohnManko Tôi đã xem xét thêm về điều này và tôi thấy từ PipedInputStreamJavadocs: Một đường ống được cho là bị hỏng nếu một luồng cung cấp byte dữ liệu cho luồng đầu ra được kết nối không còn tồn tại. Vì vậy, điều tôi nghi ngờ là nếu bạn đang sử dụng ví dụ trên, luồng đang hoàn thành trước khi Jax-RStiêu thụ luồng đầu vào. Đồng thời, tôi đã xem MongoDB Javadocs. GridFSDBFilecó một luồng đầu vào, vậy tại sao không chuyển nó sang Jax-RS ?
mikeho

3
@DennisCheung vâng, tất nhiên rồi. Không có gì miễn phí, nhưng chắc chắn nó sẽ nhỏ hơn bản sao 15 MB. Tối ưu hóa sẽ bao gồm sử dụng một nhóm luồng thay vì để giảm tốc độ GC với việc tạo luồng / đối tượng không đổi.
mikeho

40

Vì các luồng đầu vào và đầu ra chỉ là điểm bắt đầu và điểm kết thúc, giải pháp là lưu trữ dữ liệu tạm thời trong mảng byte. Vì vậy, bạn phải tạo trung gian ByteArrayOutputStream, từ đó bạn tạo byte[]được sử dụng làm đầu vào cho mới 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);
}

Hy vọng nó giúp.


baos.toByteArray () tạo một bản sao với System.arraycopy. Cảm ơn @mikeho vì đã chỉ ra nhà phát triển
Mitja Gustin

20

Bạn sẽ cần một lớp trung gian sẽ đệm giữa. Mỗi lần InputStream.read(byte[]...)được gọi, lớp đệm sẽ điền vào mảng byte được truyền vào với đoạn tiếp theo được truyền vào từ đó OutputStream.write(byte[]...). Vì kích thước của các khối có thể không giống nhau, nên lớp bộ điều hợp sẽ cần lưu trữ một lượng nhất định cho đến khi nó đủ để lấp đầy bộ đệm đọc và / hoặc có thể lưu trữ bất kỳ lỗi tràn bộ đệm nào.

Bài viết này có một phân tích tốt về một vài cách tiếp cận khác nhau cho vấn đề này:

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


1
cảm ơn @mckamey, phương pháp dựa trên Bộ đệm tròn chính xác là những gì tôi cần!
Hui Wang

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

2
Bạn không nên sử dụng điều này vì toByteArray()thân phương thức giống như phương thức này return Arrays.copyOf(buf, count);trả về một mảng mới.
Root G


9

Tôi đã gặp vấn đề tương tự với việc chuyển đổi a ByteArrayOutputStreamthành a ByteArrayInputStreamvà giải quyết nó bằng cách sử dụng một lớp dẫn xuất từ ByteArrayOutputStreamđó có thể trả về một ByteArrayInputStreamcái được khởi tạo với bộ đệm bên trong của ByteArrayOutputStream. Bằng cách này, không có bộ nhớ bổ sung nào được sử dụng và 'chuyển đổi' rất nhanh:

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

Tôi đặt nội dung trên github: https://github.com/nickrussler/ByteArrayInOutStream


Điều gì nếu nội dung không phù hợp với bộ đệm?
Vadimo

Sau đó, bạn không nên sử dụng ByteArrayInputStream ở vị trí đầu tiên.
Nick Russler

Giải pháp này sẽ có tất cả các byte trong bộ nhớ. Đối với các tệp nhỏ, điều này sẽ ổn nhưng sau đó bạn cũng có thể sử dụng getBytes () trên ByteArrayOutput Stream
Vadimo

1
Nếu bạn có nghĩa là toByteArray thì điều này sẽ khiến bộ đệm bên trong bị sao chép, việc này sẽ chiếm gấp đôi bộ nhớ so với cách tiếp cận của tôi. Chỉnh sửa: Ah tôi hiểu, đối với các tệp nhỏ, điều này hoạt động tất nhiên ..
Nick Russler

Mất thời gian. ByteArrayOutputStream có một phương pháp writeTo để truyền tải nội dung đến một dòng đầu ra
Tony BenBrahim

3

Thư viện io-ngoại có thể hữu ích. Ví dụ: nếu bạn muốn gzip một InputStreamcách sử dụng GZIPOutputStreamvà bạn muốn nó xảy ra đồng bộ (sử dụng kích thước bộ đệm mặc định là 8192):

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

Lưu ý rằng thư viện có phạm vi kiểm tra đơn vị 100% (đối với những gì đáng giá dĩ nhiên!) Và nằm trên Maven Central. Phụ thuộc Maven là:

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

Hãy chắc chắn để kiểm tra phiên bản mới hơn.


0

Theo quan điểm của tôi, java.io.PipedInputStream / java.io.PipedOutputStream là lựa chọn tốt nhất để xem xét. Trong một số tình huống, bạn có thể muốn sử dụng ByteArrayInputStream / ByteArrayOutputStream. Vấn đề là bạn cần sao chép bộ đệm để chuyển đổi ByteArrayOutputStream thành ByteArrayInputStream. Ngoài ra ByteArrayOutpuStream / ByteArrayInputStream được giới hạn ở mức 2GB. Đây là một triển khai OutpuStream / InputStream mà tôi đã viết để bỏ qua các giới hạn ByteArrayOutputStream / ByteArrayInputStream (mã Scala, nhưng dễ hiểu cho các nhà phát triển 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
      }
    }
  }
}

Dễ sử dụng, không trùng lặp bộ đệm, không giới hạn bộ nhớ 2GB

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

Nếu bạn muốn tạo một OutputStream từ InputStream, có một vấn đề cơ bản. Một phương thức ghi vào một khối OutputStream cho đến khi hoàn thành. Vì vậy, kết quả có sẵn khi phương pháp viết kết thúc. Điều này có 2 hậu quả:

  1. Nếu bạn chỉ sử dụng một luồng, bạn cần đợi cho đến khi mọi thứ được ghi (vì vậy bạn cần lưu trữ dữ liệu của luồng trong bộ nhớ hoặc đĩa).
  2. Nếu bạn muốn truy cập dữ liệu trước khi kết thúc, bạn cần một luồng thứ hai.

Biến thể 1 có thể được thực hiện bằng cách sử dụng mảng byte hoặc nộp. Biến thể 1 có thể được thực hiện bằng cách sử dụng các đường ống (trực tiếp hoặc có thêm sự trừu tượng hóa - ví dụ RingBuffer hoặc google lib từ bình luận khác).

Thật vậy, với java tiêu chuẩn, không có cách nào khác để giải quyết vấn đề. Mỗi giải pháp là một triển khai của một trong số đó.

Có một khái niệm gọi là "tiếp tục" (xem wikipedia để biết chi tiết). Trong trường hợp này về cơ bản điều này có nghĩa là:

  • có một luồng đầu ra đặc biệt mong đợi một lượng dữ liệu nhất định
  • nếu đạt được số lượng đạn, luồng sẽ kiểm soát đối tác của nó, đó là luồng đầu vào đặc biệt
  • luồng đầu vào làm cho lượng dữ liệu có sẵn cho đến khi nó được đọc, sau đó, nó chuyển trở lại điều khiển cho luồng đầu ra

Mặc dù một số ngôn ngữ có khái niệm này được tích hợp sẵn, nhưng đối với java bạn cần một số "phép thuật". Ví dụ: "commons-javaflow" từ apache thực hiện như java. Nhược điểm là điều này đòi hỏi một số sửa đổi mã byte đặc biệt tại thời điểm xây dựng. Vì vậy, sẽ rất hợp lý khi đặt tất cả nội dung vào một thư viện bổ sung với các tập lệnh xây dựng tùy chỉnh.


-1

Bài cũ nhưng có thể giúp người khác, Sử dụng cách này:

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

1
đến Chuỗi -> vấn đề kích thước
user1594895

Ngoài ra, việc gọi toString().getBytes()một luồng * sẽ không trả về nội dung của luồng.
Maarten Bodewes

-1

Mặc dù bạn không thể chuyển đổi OutputStream thành InputStream, java cung cấp cách sử dụng PipedOutputStream và PipedInputStream mà bạn có thể có dữ liệu được ghi vào PipedOutputStream để có sẵn thông qua PipedInputStream.
Thỉnh thoảng tôi gặp phải một tình huống tương tự khi giao dịch với các thư viện bên thứ ba yêu cầu một thể hiện InputStream được truyền cho họ thay vì một thể hiện OutputStream.
Cách tôi khắc phục vấn đề này là sử dụng PipedInputStream và PipedOutputStream.
Bằng cách này, chúng rất khó sử dụng và bạn phải sử dụng đa luồng để đạt được những gì bạn muốn. Gần đây tôi đã xuất bản một triển khai trên github mà bạn có thể sử dụng.
Đây là liên kết . Bạn có thể đi qua wiki để hiểu cách sử dụng nó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.