我在开发阶段,在那里我有两个模块,从一个我得到输出作为一个OutputStream和第二个,它只接受InputStream。你知道如何将OutputStream转换为InputStream(反之亦然,我的意思是真的这样),我将能够连接这两个部分吗?

谢谢


当前回答

由于输入和输出流只是起点和终点,解决方案是将数据临时存储在字节数组中。因此,您必须创建中间ByteArrayOutputStream,从中创建字节[],用作新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);
}

希望能有所帮助。

其他回答

输出流是将数据写入其中的流。如果某个模块公开了一个OutputStream,则期望在另一端有一些内容正在读取。

另一方面,公开InputStream的内容表明您将需要侦听此流,并且将有您可以读取的数据。

因此,可以将InputStream连接到OutputStream

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

正如有人提到的,这就是IOUtils的copy()方法让您做的事情。相反的方向是没有意义的。希望这能让你们理解

更新:

当然,我越想这一点,就越能看出这实际上是一个要求。我知道有些评论提到了管道输入/输出流,但还有另一种可能性。

如果公开的输出流是bybyterayoutputstream,那么您总是可以通过调用toByteArray()方法来获得完整的内容。然后,您可以使用ByteArrayInputStream子类创建输入流包装器。这两个是伪流,它们基本上都只是包装一个字节数组。因此,以这种方式使用流在技术上是可行的,但对我来说还是很奇怪……

你需要一个中间类来缓冲。每次调用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开源库直接支持将OutputStream转换为InputStream: http://io-tools.sourceforge.net/easystream/tutorial/tutorial.html

// create conversion
final OutputStreamToInputStream<Void> out = new OutputStreamToInputStream<Void>() {
    @Override
    protected Void doRead(final InputStream in) throws Exception {
           LibraryClass2.processDataFromInputStream(in);
           return null;
        }
    };
try {   
     LibraryClass1.writeDataToTheOutputStream(out);
} finally {
     // don't miss the close (or a thread would not terminate correctly).
     out.close();
}

他们还列出了其他选项:http://io-tools.sourceforge.net/easystream/outputstream_to_inputstream/implementations.html

Write the data the data into a memory buffer (ByteArrayOutputStream) get the byteArray and read it again with a ByteArrayInputStream. This is the best approach if you're sure your data fits into memory. Copy your data to a temporary file and read it back. Use pipes: this is the best approach both for memory usage and speed (you can take full advantage of the multi-core processors) and also the standard solution offered by Sun. Use InputStreamFromOutputStream and OutputStreamToInputStream from the easystream library.

似乎有许多链接和其他类似的东西,但没有使用管道的实际代码。使用java.io.PipedInputStream和java.io.PipedOutputStream的优点是不会额外消耗内存。ByteArrayOutputStream.toByteArray()返回原始缓冲区的副本,因此这意味着无论内存中有什么,现在都有它的两个副本。然后写入InputStream意味着现在有了数据的三个副本。

使用lambdas的代码(从评论中向@John Manko致敬):

PipedInputStream in = new PipedInputStream();
final PipedOutputStream out = new PipedOutputStream(in);
// in a background thread, write the given output stream to the
// PipedOutputStream for consumption
new Thread(() -> {originalOutputStream.writeTo(out);}).start();

@John Manko注意到的一件事是,在某些情况下,当您无法控制OutputStream的创建时,您可能会在创建者过早地清理OutputStream对象的情况下结束。如果你正在获取ClosedPipeException,那么你应该尝试反向构造函数:

PipedInputStream in = new PipedInputStream(out);
new Thread(() -> {originalOutputStream.writeTo(out);}).start();

注意,您也可以为下面的示例反转构造函数。

也感谢@AlexK纠正我开始一个线程,而不是仅仅启动一个Runnable。


使用try-with-resources的代码:

// take the copy of the stream and re-write it to an InputStream
PipedInputStream in = new PipedInputStream();
    new Thread(new Runnable() {
        public void run () {
            // try-with-resources here
            // putting the try block outside the Thread will cause the
            // PipedOutputStream resource to close before the Runnable finishes
            try (final PipedOutputStream out = new PipedOutputStream(in)) {
                // 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
            }
        }
    }).start();

我写的原始代码:

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

这段代码假设原始ByteArrayOutputStream是一个ByteArrayOutputStream,因为它通常是唯一可用的输出流,除非您正在写入文件。这样做的好处是,因为它在一个单独的线程中,所以它也是并行工作的,所以无论消耗你的输入流的是什么,它也会从你的旧输出流中流出。这是有益的,因为缓冲区可以保持更小,您将有更少的延迟和内存使用。

如果你没有ByteArrayOutputStream,那么你将不得不使用java.io.OutputStream类中的write()方法之一或子类中可用的其他方法之一,而不是使用writeTo()。