OutputStream을 InputStream로 변환하는 방법?


337

나는 두 개의 모듈이 개발 단계에이고 하나 나는 같은 출력을 가지고 OutputStream만 수락하고 두 번째를 InputStream. 당신은 변환하는 방법을 알고 계십니까 OutputStreamInputStream나는이 두 부분을 연결 할 수있을 것입니다 (정말이 방법을 의미 반대하지)?

감사



3
@ c0mrade, op는 다른 방향으로 만 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

누군가가 언급했듯이, 이것은 IOUtilscopy()방법으로 가능합니다. 다른 방향으로가는 것은 이치에 맞지 않습니다.

최신 정보:

물론 이것을 더 많이 생각할수록 이것이 실제로 어떻게 요구되는지 알 수 있습니다. Piped입력 / 출력 스트림에 대해 언급 한 의견 중 일부는 알고 있지만 다른 가능성이 있습니다.

노출 된 출력 스트림이 인 경우 ByteArrayOutputStream항상 toByteArray()메서드 를 호출하여 전체 내용을 가져올 수 있습니다 . 그런 다음 ByteArrayInputStream서브 클래스를 사용하여 입력 스트림 랩퍼를 작성할 수 있습니다 . 이 두 가지는 의사 스트림이며 기본적으로 바이트 배열을 래핑합니다. 따라서이 방식으로 스트림을 사용하는 것은 기술적으로 가능하지만 나에게는 여전히 매우 이상합니다 ...


4
copy () API에 따라 OS에이 작업을 수행하려면 뒤로 수행해야합니다.
Waypoint

1
상단에 내 편집 내용을 참조하십시오, 그것은 약간의 변환을 만드는 것이 필요합니다
Waypoint

88
유스 케이스는 매우 간단합니다. 직렬화 라이브러리 (예 : JSON으로 직렬화)와 InputStream을 사용하는 전송 계층 (예 : Tomcat)이 있다고 가정하십시오. 따라서 InputStream에서 읽으려는 HTTP 연결을 통해 JSON에서 OutputStream을 파이프해야합니다.
JBCP

6
이것은 단위 테스트를 할 때 유용하며 파일 시스템을 건드리지 않도록하는 데 매우 도움이됩니다.
Jon

28
@JBCP의 코멘트가 자리 잡고 있습니다. 다른 사용 사례는 HTTP 요청 중에 PDFBox를 사용하여 PDF를 작성하는 것입니다. PDF 객체는 OutputStream을 사용하여 PDF 객체를 저장하고 REST API는 InputStream을 받아 클라이언트에 응답합니다. 따라서 OutputStream-> InputStream은 매우 실제적인 사용 사례입니다.
John Manko

200

많은 링크와 다른 것들이 있지만 파이프를 사용하는 실제 코드는없는 것 같습니다. 사용의 장점 java.io.PipedInputStreamjava.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();

이 코드는이 있다고 가정 originalByteArrayOutputStreamA는 ByteArrayOutputStream이 파일에 사용자를 제외하고있는 거 쓰기, 일반적으로 만 사용할 수있는 출력 스트림 때문이다. 이게 도움이 되길 바란다! 이것에 대한 좋은 점은 별도의 스레드에 있기 때문에 병렬로도 작동하므로 입력 스트림을 소비하는 모든 것이 이전 출력 스트림에서도 스트리밍된다는 것입니다. 이는 버퍼가 더 작게 유지 될 수 있고 대기 시간과 메모리 사용량이 적기 때문에 유리합니다.


21
나는 이것을 투표했지만 의 생성자 에게 전달 out하는 것이 낫습니다 in. 그렇지 않으면 in경쟁 조건 (내가 경험 한)으로 인해 닫힌 파이프 예외가 발생할 수 있습니다. Java 8 Lambdas 사용 :PipedInputStream in = new PipedInputStream(out); ((Runnable)() -> {originalOutputStream.writeTo(out);}).run(); return in;
John Manko

1
@ JohnManko 흠 ... 나는 그런 문제가 없었습니다. 다른 스레드 또는 기본 스레드가 out.close ()를 호출하고 있기 때문에이 문제가 발생 했습니까? 이 코드는 PipedOutputStream이 originalOutputStream실제 보다 오래 수명이 있다고 가정하지만 스트림을 제어하는 ​​방법을 가정하지는 않습니다. 그것은 개발자에게 맡겨져 있습니다. 이 코드에는 파이프가 닫히거나 깨진 예외가 발생하지 않습니다.
mikeho

3
아니요, Mongo GridFS에 PDF를 저장 한 다음 Jax-RS를 사용하여 클라이언트로 스트리밍 할 때 발생합니다. MongoDB는 OutputStream을 제공하지만 Jax-RS에는 InputStream이 필요합니다. 내 경로 메서드는 OutputStream이 완전히 설정되기 전에 InputStream이있는 컨테이너로 돌아갑니다. 아마도 버퍼가 아직 캐시되지 않은 것 같습니다. 어쨌든 Jax-RS는 InputStream에서 파이프 닫힌 예외를 throw합니다. 홀수, 그러나 그것은 반 시간 동안 일어난 일입니다. 위의 코드로 변경하면 방지 할 수 있습니다.
John Manko

1
@JohnManko 나는 이것을 더 많이보고 있었고 PipedInputStreamJavadocs 에서 보았다 : 연결된 파이프 출력 스트림에 데이터 바이트를 제공하는 스레드가 더 이상 살아 있지 않으면 파이프가 고장 났다고합니다. 그래서 내가 의심하는 것은 위의 예제를 사용하면 스레드가 Jax-RS입력 스트림을 소비 하기 전에 완료되고 있다는 것 입니다. 동시에 MongoDB Javadocs를 살펴 보았습니다 . GridFSDBFile입력 스트림이 있으므로 Jax-RS로 전달하는 것이 어떻습니까?
mikeho

3
@DennisCheung 물론입니다. 무료는 없지만 15MB보다 작을 것입니다. 지속적인 스레드 / 객체 생성을 통해 GC 이탈을 줄이기 위해 스레드 풀을 사용하는 최적화가 포함됩니다.
mikeho

40

입력 및 출력 스트림은 시작 및 끝 지점이므로 데이터를 바이트 배열로 임시 저장하는 것이 해결책입니다. 따라서 new에 대한 입력으로 사용 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/...
미챠 Gustin

20

버퍼링 할 중간 클래스가 필요합니다. InputStream.read(byte[]...)호출 될 때마다 버퍼링 클래스는 전달 된 다음 청크로 전달 된 바이트 배열을 채 웁니다 OutputStream.write(byte[]...). 청크의 크기가 동일하지 않을 수 있으므로, 어댑터 클래스는 읽기 버퍼를 채울 수있을 때까지 및 / 또는 버퍼 오버 플로우를 저장할 수있을 때까지 일정량을 저장해야합니다.

이 기사에는이 문제에 대한 몇 가지 다른 접근 방식이 자세히 설명되어 있습니다.

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


1
@mckamey 덕분에, 원형 버퍼를 기반으로 한 방법이 정확히 필요합니다!
Hui Wang

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

2
toByteArray()메소드 본문은 return Arrays.copyOf(buf, count);새로운 배열을 반환하는 것과 같으 므로 이것을 사용해서는 안됩니다 .
Root G


9

a ByteArrayOutputStream를 a 로 변환하는 것과 동일한 문제가 발생하여 의 내부 버퍼로 초기화 ByteArrayInputStream된를 ByteArrayOutputStream반환 할 수 있는 파생 클래스를 사용하여 해결했습니다 . 이렇게하면 추가 메모리가 사용되지 않고 '변환'이 매우 빠릅니다.ByteArrayInputStreamByteArrayOutputStream

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


내용이 버퍼에 맞지 않으면 어떻게됩니까?
Vadimo

그런 다음 처음에는 ByteArrayInputStream을 사용하지 않아야합니다.
Nick Russler

이 솔루션에는 메모리에 모든 바이트가 있습니다. 작은 파일의 경우 문제가 없지만 ByteArrayOutput Stream에서 getBytes ()를 사용할 수도 있습니다
Vadimo

1
toByteArray 를 의미하는 경우 내부 버퍼가 복사되어 내 접근 방식보다 두 배의 메모리가 필요합니다. 편집 : 아 이해, 작은 파일에 대한 물론 작동합니다 ..
Nick Russler

시간 낭비. 를 ByteArrayOutputStream 다른 출력 스트림에 전달 콘텐츠로는, writeTo 메소드가
토니 BenBrahim에게

3

라이브러리 io-extras 가 유용 할 수 있습니다. 예를 들어, gzip을 InputStream사용 GZIPOutputStream하고 동 기적 으로 일어나길 원한다면 (기본 버퍼 크기 8192 사용) :

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

라이브러리는 100 % 단위 테스트 적용 범위 (물론 가치가 있습니다!)를 가지고 있으며 Maven Central에 있습니다. Maven 종속성은 다음과 같습니다.

<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은 2GB로 제한됩니다. 다음은 ByteArrayOutputStream / ByteArrayInputStream 제한 (Scala 코드이지만 Java 개발자가 쉽게 이해할 수 있음)을 무시하기 위해 작성한 OutpuStream / InputStream 구현입니다.

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

사용하기 쉽고 버퍼 복제가 없으며 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

InputStream에서 OutputStream을 만들려면 하나의 기본 문제가 있습니다. OutputStream에 쓰는 메소드는 완료 될 때까지 차단합니다. 따라서 쓰기 방법이 끝나면 결과를 사용할 수 있습니다. 이것은 두 가지 결과가 있습니다 :

  1. 하나의 스레드 만 사용하는 경우 모든 것이 기록 될 때까지 기다려야합니다 (따라서 스트림의 데이터를 메모리 또는 디스크에 저장해야 함).
  2. 완료되기 전에 데이터에 액세스하려면 두 번째 스레드가 필요합니다.

변형 1은 바이트 배열을 사용하여 구현하거나 제출할 수 있습니다. 변형 1은 pipies (직접 또는 추가 추상화 (예 : RingBuffer 또는 다른 주석의 google lib))를 사용하여 구현할 수 있습니다.

실제로 표준 Java에서는 문제를 해결할 다른 방법이 없습니다. 각 솔루션은 이들 중 하나의 구현입니다.

"continuation"이라는 개념이 있습니다 (자세한 내용은 wikipedia 참조). 이 경우 기본적으로 이것은 다음을 의미합니다.

  • 특정 양의 데이터를 예상하는 특수 출력 스트림이 있습니다.
  • 양에 도달하면 스트림이 특수 입력 스트림 인 상대방에 대한 제어를 제공합니다.
  • 입력 스트림은 읽을 때까지 사용 가능한 데이터 양을 만들고, 그 후에는 제어를 출력 스트림으로 다시 전달합니다.

일부 언어에는이 개념이 내장되어 있지만 Java의 경우 "마법"이 필요합니다. 예를 들어 아파치의 "commons-javaflow"는 자바를 위해 구현합니다. 단점은 빌드시 특별한 바이트 코드 수정이 필요하다는 것입니다. 따라서 사용자 정의 빌드 스크립트와 함께 모든 라이브러리를 추가 라이브러리에 넣는 것이 좋습니다.


-1

오래된 게시물이지만 다른 사람을 도울 수 있습니다.이 방법을 사용하십시오.

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

1
to String-> size problem
user1594895

또한 toString().getBytes()스트림을 호출 하면 스트림의 내용이 반환되지 않습니다.
Maarten Bodewes

-1

OutputStream을 InputStream으로 변환 할 수는 없지만 java는 PipedOutputStream 및 PipedInputStream을 사용하여 PipedOutputStream에 데이터를 기록하여 연관된 PipedInputStream을 통해 사용 가능하게하는 방법을 제공합니다.
언젠가 InputStream 인스턴스가 OutputStream 인스턴스 대신 전달되어야하는 타사 라이브러리를 처리 할 때 비슷한 상황에 직면했습니다.
이 문제를 해결 한 방법은 PipedInputStream 및 PipedOutputStream을 사용하는 것입니다.
그건 그렇고 그들은 사용하기 까다 롭고 원하는 것을 달성하기 위해 멀티 스레딩을 사용해야합니다. 최근에 사용할 수있는 github에 구현을 게시했습니다.
여기 링크가 있습니다. 위키를 통해 사용법을 이해할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.