1. 概述
处理流是编程中常见的任务,特别是在进行输入输出操作时。有时,我们需要将OutputStream
转换为字节数组,这在诸如网络编程、文件处理或数据操纵等场景中非常有用。
本教程将探讨两种方法来实现这个转换。
2. 使用Apache Commons IO库的FileUtils
类
Apache Commons IO库提供了FileUtils
类,其中包含readFileToByteArray()
方法,可以间接将FileOutputStream
转换为字节数组。这是通过首先写入文件,然后从文件系统读取结果字节实现的。
要在项目中使用此库,我们需要先在项目中添加以下 Commons IO 的 依赖项:
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
让我们看一个简单的示例来实现这个功能:
@Test
public void givenFileOutputStream_whenUsingFileUtilsToReadTheFile_thenReturnByteArray(@TempDir Path tempDir) throws IOException {
String data = "Welcome to Baeldung!";
String fileName = "file.txt";
Path filePath = tempDir.resolve(fileName);
try (FileOutputStream outputStream = new FileOutputStream(filePath.toFile())) {
outputStream.write(data.getBytes(StandardCharsets.UTF_8));
}
byte[] writtenData = FileUtils.readFileToByteArray(filePath.toFile());
String result = new String(writtenData, StandardCharsets.UTF_8);
assertEquals(data, result);
}
在上述测试方法中,我们初始化了一个字符串data
和一个filePath
。接着,我们使用FileOutputStream
将字符串的字节表示写入文件。随后,我们使用FileUtils.readFileToByteArray()
方法有效地将文件内容转换为字节数组。
最后,将字节数组转换回字符串,并通过断言确认原始的data
和result
相等。
值得注意的是,这种方法仅适用于FileOutputStream
,因为只有在流关闭后,我们才能检查写入的文件。对于更通用的解决方案,适用于不同类型的OutputStream
,下一节将介绍一种提供更广泛适用性的替代方法。
3. 使用自定义的DrainableOutputStream
另一种方法是创建一个自定义的DrainableOutputStream
类,它扩展了FilterOutputStream
。这个类会在将字节写入底层OutputStream
的同时,在内存中保留一份副本,从而允许稍后转换为字节数组。
首先,让我们创建一个自定义的DrainableOutputStream
类,它扩展了FilterOutputStream
:
public class DrainableOutputStream extends FilterOutputStream {
private final ByteArrayOutputStream buffer;
public DrainableOutputStream(OutputStream out) {
super(out);
this.buffer = new ByteArrayOutputStream();
}
@Override
public void write(byte b[]) throws IOException {
buffer.write(b);
super.write(b);
}
public byte[] toByteArray() {
return buffer.toByteArray();
}
}
在上面的代码中,我们首先声明一个类DrainableOutputStream
,它会包装给定的OutputStream
。我们包括一个ByteArrayOutputStream
缓冲区buffer
来积累写入的字节,并重写write()
方法以捕获字节。我们还实现了toByteArray()
方法以提供访问捕获的字节的途径。
现在,让我们使用DrainableOutputStream
:
@Test
public void givenSystemOut_whenUsingDrainableOutputStream_thenReturnByteArray() throws IOException {
String data = "Welcome to Baeldung!\n";
DrainableOutputStream drainableOutputStream = new DrainableOutputStream(System.out);
try (drainableOutputStream) {
drainableOutputStream.write(data.getBytes(StandardCharsets.UTF_8));
}
byte[] writtenData = drainableOutputStream.toByteArray();
String result = new String(writtenData, StandardCharsets.UTF_8);
assertEquals(data, result);
}
在上述测试方法中,我们首先初始化一个想要写入OutputStream
的字符串data
。然后,我们利用DrainableOutputStream
来拦截这个过程,通过在实际写入到OutputStream
之前捕获字节。随后,我们使用toByteArray()
方法将累积的字节转换为字节数组。
接着,我们根据捕获的字节数组创建一个新的字符串result
,并断言其与原始data
相等。
请注意,一个全面的DrainableOutputStream
实现还需要为除了展示的示例之外的其他写入方法提供类似的重写。
4. 考虑因素和限制
虽然前几节中介绍的方法为将OutputStream
转换为字节数组提供了实用的途径,但有必要认识到这项任务相关的某些考虑因素和限制。
将任意OutputStream
转换为字节数组通常不是一个直接的操作,因为在字节被写入之后,可能无法或不切实际地获取它们。
某些子类,如FileOutputStream
或ByteArrayOutputStream
,有内置机制允许我们获取输出字节,例如内存缓冲区或已写入的文件。然而,如果没有这样的输出字节副本,我们可能需要考虑使用类似DrainableOutputStream
的技术,在写入字节时进行复制。
5. 总结
总之,编程中有这样的情景:将OutputStream
转换为Java中的字节数组是很有用的操作。我们已经看到了如何使用Apache Commons IO库的FileUtils.readFileToByteArray()
方法读取FileOutputStream
生成的文件,以及使用自定义的DrainableOutputStream
来为特定的OutputStream
获取写入字节的更通用方法。
如往常一样,配套的源代码可以在 GitHub 上找到。