OutputStream을 InputStream로 변환하는 방법?
나는 두 개의 모듈이 개발 단계에이고 하나 나는 같은 출력을 가지고 OutputStream
만 수락하고 두 번째를 InputStream
. 당신은 변환하는 방법을 알고 계십니까 OutputStream
에 InputStream
나는이 두 부분을 연결 할 수있을 것입니다 (정말이 방법을 의미 반대하지)?
감사
은 OutputStream
당신이 데이터를 쓸 것입니다. 일부 모듈이을 노출 OutputStream
하면 다른 쪽 끝에 무언가가있을 것으로 예상됩니다.
를 노출 뭔가 InputStream
, 다른 한편으로는, 당신이 스트림을 청취 할 필요가 있음을 나타내는하며, 당신이 읽을 수있는 데이터가있을 것입니다.
따라서에 연결할 InputStream
수 있습니다OutputStream
InputStream----read---> intermediateBytes[n] ----write----> OutputStream
누군가가 언급했듯이, 이것은 IOUtils 의 copy()
방법으로 가능합니다. 다른 방향으로가는 것은 이치에 맞지 않습니다.
최신 정보:
물론 이것을 더 많이 생각할수록 이것이 실제로 어떻게 요구되는지 알 수 있습니다. Piped
입력 / 출력 스트림에 대해 언급 한 의견 중 일부는 알고 있지만 다른 가능성이 있습니다.
노출 된 출력 스트림이 인 경우 ByteArrayOutputStream
항상 toByteArray()
메서드 를 호출하여 전체 내용을 가져올 수 있습니다 . 그런 다음 ByteArrayInputStream
서브 클래스를 사용하여 입력 스트림 랩퍼를 작성할 수 있습니다 . 이 두 가지는 의사 스트림이며 기본적으로 바이트 배열을 래핑합니다. 따라서이 방식으로 스트림을 사용하는 것은 기술적으로 가능하지만 나에게는 여전히 매우 이상합니다 ...
많은 링크와 다른 것들이 있지만 파이프를 사용하는 실제 코드는없는 것 같습니다. 사용의 장점 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
A는 ByteArrayOutputStream
이 파일에 사용자를 제외하고있는 거 쓰기, 일반적으로 만 사용할 수있는 출력 스트림 때문이다. 이게 도움이 되길 바란다! 이것에 대한 좋은 점은 별도의 스레드에 있기 때문에 병렬로도 작동하므로 입력 스트림을 소비하는 모든 것이 이전 출력 스트림에서도 스트리밍된다는 것입니다. 이는 버퍼가 더 작게 유지 될 수 있고 대기 시간과 메모리 사용량이 적기 때문에 유리합니다.
입력 및 출력 스트림이 시작 및 끝 지점이므로 데이터를 바이트 배열로 임시 저장하는 것이 해결책입니다. 따라서 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);
}
도움이 되길 바랍니다.
버퍼링 할 중간 클래스가 필요합니다. InputStream.read(byte[]...)
호출 될 때마다 버퍼링 클래스는 전달 된 다음 청크로 전달 된 바이트 배열을 채 웁니다 OutputStream.write(byte[]...)
. 청크의 크기가 동일하지 않을 수 있으므로, 어댑터 클래스는 읽기 버퍼를 채울 수있을 때까지 및 / 또는 버퍼 오버 플로우를 저장할 수있을 때까지 일정량을 저장해야합니다.
이 기사에는이 문제에 대한 몇 가지 다른 접근 방식이 잘 정리되어 있습니다.
http://blog.ostermiller.org/convert-java-outputstream-inputstream
ByteArrayOutputStream buffer = (ByteArrayOutputStream) aOutputStream;
byte[] bytes = buffer.toByteArray();
InputStream inputStream = new ByteArrayInputStream(bytes);
easystream 오픈 소스 라이브러리는의 InputStream에 OutputStream를 변환하는 직접 지원이 있습니다 http://io-tools.sourceforge.net/easystream/tutorial/tutorial.html
그들은 또한 다른 옵션을 나열합니다 : http://io-tools.sourceforge.net/easystream/OutputStream_to_InputStream.html
a ByteArrayOutputStream
를 a 로 변환하는 것과 동일한 문제가 발생하여 의 내부 버퍼로 초기화 ByteArrayInputStream
된를 ByteArrayOutputStream
반환 할 수 있는 파생 클래스를 사용하여 해결했습니다 . 이 방법으로 추가 메모리를 사용하지 않고 '변환'이 매우 빠릅니다.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
라이브러리 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>
이후 버전을 확인하십시오.
필자의 견해로는 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(...)
...
InputStream에서 OutputStream을 만들려면 하나의 기본 문제가 있습니다. OutputStream에 쓰는 메소드는 완료 될 때까지 차단합니다. 따라서 쓰기 방법이 끝나면 결과를 사용할 수 있습니다. 이것은 두 가지 결과가 있습니다 :
- 하나의 스레드 만 사용하는 경우 모든 것이 기록 될 때까지 기다려야합니다 (따라서 스트림의 데이터를 메모리 또는 디스크에 저장해야 함).
- 완료되기 전에 데이터에 액세스하려면 두 번째 스레드가 필요합니다.
변형 1은 바이트 배열을 사용하여 구현하거나 제출할 수 있습니다. 변형 1은 pipies (직접 또는 추가 추상화 (예 : RingBuffer 또는 다른 주석의 google lib))를 사용하여 구현할 수 있습니다.
실제로 표준 Java에서는 문제를 해결할 다른 방법이 없습니다. 각 솔루션은 이들 중 하나의 구현입니다.
"계속"이라는 개념이 있습니다 (자세한 내용은 wikipedia 참조). 이 경우 기본적으로 이것은 다음을 의미합니다.
- 특정 양의 데이터를 예상하는 특수 출력 스트림이 있습니다.
- 양에 도달하면 스트림이 특수 입력 스트림 인 상대방에 대한 제어를 제공합니다.
- 입력 스트림은 읽을 때까지 사용 가능한 데이터 양을 만들고, 그 후에는 제어를 출력 스트림으로 다시 전달합니다.
일부 언어에는이 개념이 내장되어 있지만 Java의 경우 "마법"이 필요합니다. 예를 들어 아파치의 "commons-javaflow"는 자바를 위해 구현합니다. 단점은 빌드시 특별한 바이트 코드 수정이 필요하다는 것입니다. 따라서 사용자 정의 빌드 스크립트와 함께 모든 라이브러리를 추가 라이브러리에 넣는 것이 좋습니다.
오래된 게시물이지만 다른 사람을 도울 수 있습니다.이 방법을 사용하십시오.
OutputStream out = new ByteArrayOutputStream();
...
out.write();
...
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(out.toString().getBytes()));
OutputStream을 InputStream으로 변환 할 수는 없지만 java는 PipedOutputStream 및 PipedInputStream을 사용하여 PipedOutputStream에 데이터를 기록하여 연관된 PipedInputStream을 통해 사용할 수있는 방법을 제공합니다.
언젠가 InputStream 인스턴스가 OutputStream 인스턴스 대신 전달되어야하는 타사 라이브러리를 처리 할 때 비슷한 상황에 직면했습니다.
이 문제를 해결 한 방법은 PipedInputStream 및 PipedOutputStream을 사용하는 것입니다.
그건 그렇고 그들은 사용하기 까다 롭고 원하는 것을 달성하기 위해 멀티 스레딩을 사용해야합니다. 최근에 사용할 수있는 github에 구현을 게시했습니다.
여기 링크가 있습니다. 위키를 통해 사용법을 이해할 수 있습니다.
참고 URL : https://stackoverflow.com/questions/5778658/how-to-convert-outputstream-to-inputstream
'development' 카테고리의 다른 글
GitHub가 SSH를 통한 HTTPS를 권장하는 이유는 무엇입니까? (0) | 2020.03.14 |
---|---|
“.NET Core”란 무엇입니까? (0) | 2020.03.14 |
"Catch SyntaxError : Unexpected token o"가 계속 나타납니다. (0) | 2020.03.14 |
동기화 된 (this)을 사용할 수 있다면 왜 ReentrantLock을 사용합니까? (0) | 2020.03.14 |
Git 브랜치를 검색하여 파일이나 디렉토리를 찾으려면 어떻게해야합니까? (0) | 2020.03.14 |