如何将OutputStream转换为InputStream?

Way*_*int 308 java inputstream outputstream

我在开发阶段,我有两个模块,从一个我得到输出作为OutputStream第二个,只接受InputStream.你知道如何转换OutputStreamInputStream(而不是相反,我是说真的这样),我将能够这两部分连接?

谢谢

mik*_*eho 180

似乎有很多链接和其他类似的东西,但没有使用管道的实际代码.使用的优点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();
Run Code Online (Sandbox Code Playgroud)

此代码假定它originalByteArrayOutputStream是a,ByteArrayOutputStream因为它通常是唯一可用的输出流,除非您正在写入文件.我希望这有帮助!关于这一点的好处是,因为它在一个单独的线程中,它也是并行工作的,所以无论消耗什么输入流都将流出旧的输出流.这是有益的,因为缓冲区可以保持较小,并且您将具有更少的延迟和更少的内存使用.

  • 我投了这个,但最好将`out`传递给`in`的构造函数,否则你可能因为竞争条件(我经历过)而在`in`上得到一个封闭的管道异常.使用Java 8 Lambdas:`PipedInputStream in = new PipedInputStream(out); ((Runnable)() - > {originalOutputStream.writeTo(out);}).run(); 回来;` (19认同)
  • 请记住,PipedInputStream 和 PipedOutputStream 需要位于单独的线程中,否则在达到一定大小后可能会发生死锁(请参阅 Java 文档:https://docs.oracle.com/javase/7/docs/api/java/ io/PipedInputStream.html) (6认同)
  • 不,我的情况源于我在Mongo GridFS中存储PDF,然后使用Jax-RS流式传输到客户端.MongoDB提供OutputStream,但Jax-RS需要InputStream.我的路径方法将在OutputStream完全建立之前返回带有InputStream的容器,似乎(可能缓冲区尚未缓存).无论如何,Jax-RS会在InputStream上抛出管道关闭异常.奇怪,但那是一半时间发生的事情.更改为上面的代码可防止这种情况. (3认同)
  • @DennisCheung是的,当然.没有什么是免费的,但它肯定会小于15MB的副本.优化包括使用线程池来减少GC流失,并创建持续的线程/对象. (3认同)
  • @JohnManko 我正在进一步研究这个问题,我从```PipedInputStream``` Javadocs 中看到: *如果向连接的管道输出流提供数据字节的线程不再存在,则管道被破坏。*所以我怀疑的是,如果你使用上面的例子,线程在 ```Jax-RS``` 消耗输入流之前完成。同时,我查看了 **MongoDB** Javadocs。```GridFSDBFile``` 有一个输入流,那么为什么不把它传递给 **Jax-RS** 呢? (2认同)
  • @JohnManko同时,初始化的顺序并不重要,所以我将更新答案以澄清这一点。我的其他评论旨在找到问题的根本原因,因为初始化顺序确实不重要。(我知道它对您有用。这对我来说表明输入流和输出流之间存在时序问题。当您的系统承受更多负载或其他因素时,这些时序将会改变,您可能会再次遇到问题.根据 Javadocs 的说法,翻转初始化不应该是一个真正的解决方案。) (2认同)
  • @MitjaGustin它确实使用了一个新线程,但与内存相比,线程相对昂贵。大多数程序都受内存限制,而不是 CPU 限制,尤其是在处理大量数据时。如果您查看源代码(http://developer.classpath.org/doc/java/io/ByteArrayOutputStream-source.html),您会发现 toByteArray() 确实复制了输出流的缓冲区。另外, toByteArray() 仅适用于 ByteArrayOutputStream,不适用于其他类型的 OutputStream (http://developer.classpath.org/doc/java/io/OutputStream.html)。我只是以BAOS为例。 (2认同)

Jav*_*ker 105

一个OutputStream是一个,你将数据写入.如果某个模块暴露了一个OutputStream,那么期望在另一端有一些东西读取.

InputStream另一方面,暴露出来的东西表明你需要收听这个流,并且会有你可以阅读的数据.

所以可以连接InputStream一个OutputStream

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

正如有人提到的,这就是IOUtils允许你做的copy()方法.走另一条路是没有意义的......希望这是有道理的

更新:

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

如果公开的输出流是a ByteArrayOutputStream,则可以通过调用该toByteArray()方法始终获取完整内容.然后,您可以使用ByteArrayInputStream子类创建输入流包装器.这两个是伪流,它们基本上只包装一个字节数组.因此,以这种方式使用流是技术上可行的,但对我来说它仍然很奇怪......

  • 用例非常简单:假设你有一个序列化库(例如,序列化为JSON)和一个带有InputStream的传输层(比如Tomcat).因此,您需要通过想要从InputStream读取的HTTP连接来管理来自JSON的OutputStream. (75认同)
  • @JBCP的评论是现货.另一个用例是在HTTP请求期间使用PDFBox构建PDF.PDFBox使用OutputStream保存PDF对象,REST API接受InputStream来回复客户端.因此,OutputStream - > InputStream是一个非常现实的用例. (22认同)
  • 这在单元测试时非常有用,而且您对避免触摸文件系统非常迂腐. (6认同)
  • “你总是可以通过调用 toByteArray() 方法来获取完整的内容”使用流的要点是不要将整个内容加载到内存中! (5认同)
  • copy()根据API执行此操作系统,我需要它向后执行操作 (4认同)
  • “走另一条路是没有意义的……”除非您尝试将对象写入 GCP 或 AWS 存储桶。它们的两个 API 都需要输入流来进行 WRITE 操作,大概这样它们就可以完全控制数据传输。这使得将它们插入为文件系统编写的代码变得困难。 (3认同)

小智 31

由于输入和输出流只是起点和终点,因此解决方案是以字节数组临时存储数据.因此,您必须创建中间件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);
}
Run Code Online (Sandbox Code Playgroud)

希望能帮助到你.


mck*_*mey 20

您将需要一个将在其间缓冲的中间类.每次InputStream.read(byte[]...)调用时,缓冲类将填充传入的字节数组,并传入下一个传入的块OutputStream.write(byte[]...).由于块的大小可能不同,因此适配器类将需要存储一定量,直到它足以填充读缓冲区和/或能够存储任何缓冲区溢出.

本文对这个问题的一些不同方法进行了很好的细分:

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

  • 链接已经死了. (5认同)
  • 链接现在已修复. (2认同)

Vij*_*pta 17

ByteArrayOutputStream buffer = (ByteArrayOutputStream) aOutputStream;
byte[] bytes = buffer.toByteArray();
InputStream inputStream = new ByteArrayInputStream(bytes);
Run Code Online (Sandbox Code Playgroud)

  • 你不应该使用它,因为 `toByteArray()` 方法体就像这样 `return Arrays.copyOf(buf, count);` 它返回一个新数组。 (2认同)

Nic*_*ler 9

我遇到了将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;
    }
}
Run Code Online (Sandbox Code Playgroud)

我把这些东西放在github上:https://github.com/nickrussler/ByteArrayInOutStream

  • 如果您的意思是 [toByteArray](http://docs.oracle.com/javase/7/docs/api/java/io/ByteArrayOutputStream.html#toByteArray()) 这将导致内部缓冲区被复制,这将需要内存是我方法的两倍。编辑:啊我明白,对于小文件这当然有效。 (3认同)
  • @TonyBenBrahim 但这**复制**缓冲区,这使内存使用量加倍并花费时间。 (2认同)