1. 概述

在Java I/O中,InputStreamOutputStream是两个基本类。有时,我们需要在这些流类型之间进行转换。在之前的教程中,我们已经讨论了将InputStream写入到OutputStream的过程。

在这个简短的教程中,我们将探讨如何将OutputStream转换为InputStream

2. 问题介绍

有时候,有必要将OutputStream转换为InputStream。这种转换在某些情况下会很有用,比如当我们需要读取写入到OutputStream的数据时。

在这篇文章中,我们将探讨两种不同的转换方法:

  • 使用字节数组
  • 通过管道

为了简化,我们的示例将使用ByteArrayOutputStream作为OutputStream类型。此外,我们将使用单元测试断言来验证从转换后的InputStream对象中读取到的数据是否符合预期。

接下来,让我们实际操作一下。

3. 使用字节数组

当我们思考这个问题时,最直接的方法可能是:

现在,让我们实现这个想法并进行测试:

@Test
void whenUsingByteArray_thenGetExpectedInputStream() throws IOException {
    String content = "I'm an important message.";
    try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
        out.write(content.getBytes());
        try (ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray())) {
            String inContent = new String(in.readAllBytes());

            assertEquals(content, inContent);
        }
    }
}

首先,我们准备了一个OutputStream对象(out)并向其中写入字符串(content)。接着,我们通过调用out.toByteArray()OutputStream获取数据并从数组中创建InputStream

如果运行测试,它会通过。因此,转换成功。

值得一提的是,我们使用了try-with-resources来确保InputStreamOutputStream在读写操作后被关闭。此外,我们的try块没有catch块,因为我们声明了测试方法会抛出IOException

这种方法简单易实现。然而,缺点是它需要将整个输出存储在内存中,才能作为输入读取回来。换句话说,如果输出非常大,可能会导致大量内存消耗,甚至引发OutOfMemoryError

4. 通过管道

我们经常使用PipedOutputStreamPipedInputStream类一起,以便数据可以从OutputStream传递到InputStream。因此,我们可以首先连接一个PipedOutputStream和一个PipedInputStream,使PipedInputStream能够读取来自PipedOutputStream的数据。然后,我们可以将给定OutputStream的数据写入到PipedOutputStream。最后,在另一侧,我们可以从PipedInputStream读取数据。

接下来,让我们将其实现为单元测试:

@Test
void whenUsingPipeStream_thenGetExpectedInputStream() throws IOException {
    String content = "I'm going through the pipe.";

    ByteArrayOutputStream originOut = new ByteArrayOutputStream();
    originOut.write(content.getBytes());

    //connect the pipe
    PipedInputStream in = new PipedInputStream();
    PipedOutputStream out = new PipedOutputStream(in);

    try (in) {
        new Thread(() -> {
            try (out) {
                originOut.writeTo(out);
            } catch (IOException iox) {
                // ...
            }
        }).start();

        String inContent = new String(in.readAllBytes());
        assertEquals(content, inContent);
    }
}

如代码所示,首先我们准备了OutputStreamoriginOut)。接着,我们创建了PipedInputStreamin)和PipedOutputStreamout),并将它们连接起来,从而建立了一个管道。

然后,我们使用ByteArrayOutputStream.writeTo()方法将数据从给定的OutputStream写入到PipedOutputStream。需要注意的是,我们创建了一个新线程来向PipedOutputStream写入数据。这是因为在同一线程中使用PipedInputStreamPipedOutputStream对象可能不推荐,可能导致死锁

如果执行测试,它会通过。所以OutputStream成功地转换为了PipedInputStream

5. 总结

在这篇文章中,我们探讨了将OutputStream转换为InputStream的两种方法:

  • 字节数组作为缓冲区 - 直截了当,但有引发OutOfMemoryError的风险
  • 通过管道 - 将输出写入PipedOutputStream,使得数据流向PipedInputStream

如往常一样,这里展示的所有代码片段都可以在GitHub上找到。