1. 概述
在Java I/O中,InputStream
和OutputStream
是两个基本类。有时,我们需要在这些流类型之间进行转换。在之前的教程中,我们已经讨论了将InputStream
写入到OutputStream
的过程。
在这个简短的教程中,我们将探讨如何将OutputStream
转换为InputStream
。
2. 问题介绍
有时候,有必要将OutputStream
转换为InputStream
。这种转换在某些情况下会很有用,比如当我们需要读取写入到OutputStream
的数据时。
在这篇文章中,我们将探讨两种不同的转换方法:
- 使用字节数组
- 通过管道
为了简化,我们的示例将使用ByteArrayOutputStream
作为OutputStream
类型。此外,我们将使用单元测试断言来验证从转换后的InputStream
对象中读取到的数据是否符合预期。
接下来,让我们实际操作一下。
3. 使用字节数组
当我们思考这个问题时,最直接的方法可能是:
- 步骤1:从给定的
OutputStream
读取数据,并将其保存在缓冲区中,例如字节数组 - 步骤2:使用字节数组创建一个
InputStream
现在,让我们实现这个想法并进行测试:
@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
来确保InputStream
和OutputStream
在读写操作后被关闭。此外,我们的try
块没有catch
块,因为我们声明了测试方法会抛出IOException
。
这种方法简单易实现。然而,缺点是它需要将整个输出存储在内存中,才能作为输入读取回来。换句话说,如果输出非常大,可能会导致大量内存消耗,甚至引发OutOfMemoryError
。
4. 通过管道
我们经常使用PipedOutputStream
和PipedInputStream
类一起,以便数据可以从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);
}
}
如代码所示,首先我们准备了OutputStream
(originOut
)。接着,我们创建了PipedInputStream
(in
)和PipedOutputStream
(out
),并将它们连接起来,从而建立了一个管道。
然后,我们使用ByteArrayOutputStream.writeTo()
方法将数据从给定的OutputStream
写入到PipedOutputStream
。需要注意的是,我们创建了一个新线程来向PipedOutputStream
写入数据。这是因为在同一线程中使用PipedInputStream
和PipedOutputStream
对象可能不推荐,可能导致死锁。
如果执行测试,它会通过。所以OutputStream
成功地转换为了PipedInputStream
。
5. 总结
在这篇文章中,我们探讨了将OutputStream
转换为InputStream
的两种方法:
- 字节数组作为缓冲区 - 直截了当,但有引发
OutOfMemoryError
的风险 - 通过管道 - 将输出写入
PipedOutputStream
,使得数据流向PipedInputStream
如往常一样,这里展示的所有代码片段都可以在GitHub上找到。