1. 概述
本文将探讨 如何将 InputStream 转换为字符串。我们将从纯 Java 方案开始(包括 Java 8/9 的解决方案),然后介绍如何使用 Guava 和 Apache Commons IO 库实现相同功能。
本文是 Baeldung 上 "Java – Back to Basic" 系列 的一部分。
2. 使用 Java 的 StringBuilder 转换
先看一个底层实现方案,使用纯 Java 的 InputStream 和 StringBuilder:
@Test
public void givenUsingJava5_whenConvertingAnInputStreamToAString_thenCorrect()
throws IOException {
String originalString = randomAlphabetic(DEFAULT_SIZE);
InputStream inputStream = new ByteArrayInputStream(originalString.getBytes());
StringBuilder textBuilder = new StringBuilder();
try (Reader reader = new BufferedReader(new InputStreamReader
(inputStream, StandardCharsets.UTF_8))) {
int c = 0;
while ((c = reader.read()) != -1) {
textBuilder.append((char) c);
}
}
assertEquals(textBuilder.toString(), originalString);
}
3. 使用 Java 8 的 BufferedReader 转换
Java 8 为 BufferedReader 新增了 lines()
方法。下面演示如何利用它将 InputStream 转换为 String:
@Test
public void givenUsingJava8_whenConvertingAnInputStreamToAString_thenCorrect() {
String originalString = randomAlphabetic(DEFAULT_SIZE);
InputStream inputStream = new ByteArrayInputStream(originalString.getBytes());
String text = new BufferedReader(
new InputStreamReader(inputStream, StandardCharsets.UTF_8))
.lines()
.collect(Collectors.joining("\n"));
assertThat(text, equalTo(originalString));
}
⚠️ 需要注意:
lines()
内部使用readLine()
方法,该方法支持 Unix(\n
)、Windows(\r\n
)和旧版 Mac(\r
)的换行符- 使用
Collectors.joining()
时需显式指定换行符类型,也可用Collectors.joining(System.lineSeparator())
适配系统默认换行符
4. 使用 Java 9 的 InputStream.readAllBytes() 转换
在 Java 9+ 中,可直接使用 InputStream 新增的 readAllBytes
方法:
@Test
public void givenUsingJava9_whenConvertingAnInputStreamToAString_thenCorrect() throws IOException {
String originalString = randomAlphabetic(DEFAULT_SIZE);
InputStream inputStream = new ByteArrayInputStream(originalString.getBytes());
String text = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
assertThat(text, equalTo(originalString));
}
❌ 注意: 此方法仅适用于简单场景,切勿用于处理大文件(可能导致内存溢出)
5. 使用 Java 的 Scanner 转换
下面看一个使用标准文本 Scanner 的纯 Java 实现:
@Test
public void givenUsingJava7_whenConvertingAnInputStreamToAString_thenCorrect()
throws IOException {
String originalString = randomAlphabetic(8);
InputStream inputStream = new ByteArrayInputStream(originalString.getBytes());
String text = null;
try (Scanner scanner = new Scanner(inputStream, StandardCharsets.UTF_8.name())) {
text = scanner.useDelimiter("\\A").next();
}
assertThat(text, equalTo(originalString));
}
关键点解析:
- Scanner 关闭时会自动关闭 InputStream
useDelimiter("\\A")
中的\A
是正则表达式边界标记,表示输入流的起始位置- 实际相当于一次性读取整个输入流
示例标注为 Java 7 仅因使用了 try-with-resources 语法,若改用标准 try-finally 块,Java 5 也能运行
6. 使用 ByteArrayOutputStream 转换
再看一个使用 ByteArrayOutputStream 的纯 Java 方案:
@Test
public void givenUsingPlainJava_whenConvertingAnInputStreamToString_thenCorrect()
throws IOException {
String originalString = randomAlphabetic(8);
InputStream inputStream = new ByteArrayInputStream(originalString.getBytes());
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead;
byte[] data = new byte[1024];
while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
buffer.flush();
byte[] byteArray = buffer.toByteArray();
String text = new String(byteArray, StandardCharsets.UTF_8);
assertThat(text, equalTo(originalString));
}
核心流程:
- 通过分块读写将 InputStream 转为 ByteArrayOutputStream
- 将 OutputStream 转为 byte 数组
- 用 byte 数组构建 String
7. 使用 java.nio 转换
另一种方案是 先将 InputStream 写入临时文件再转换为字符串:
@Test
public void givenUsingTempFile_whenConvertingAnInputStreamToAString_thenCorrect()
throws IOException {
String originalString = randomAlphabetic(DEFAULT_SIZE);
InputStream inputStream = new ByteArrayInputStream(originalString.getBytes());
Path tempFile =
Files.createTempDirectory("").resolve(UUID.randomUUID().toString() + ".tmp");
Files.copy(inputStream, tempFile, StandardCopyOption.REPLACE_EXISTING);
String result = new String(Files.readAllBytes(tempFile));
assertThat(result, equalTo(originalString));
}
实现步骤:
- 使用
java.nio.file.Files
创建临时文件 - 将 InputStream 内容复制到文件
- 通过
Files.readAllBytes()
读取文件内容转为字符串
8. 使用 Guava 转换
先看利用 ByteSource 功能的 Guava 实现:
@Test
public void givenUsingGuava_whenConvertingAnInputStreamToAString_thenCorrect()
throws IOException {
String originalString = randomAlphabetic(8);
InputStream inputStream = new ByteArrayInputStream(originalString.getBytes());
ByteSource byteSource = new ByteSource() {
@Override
public InputStream openStream() throws IOException {
return inputStream;
}
};
String text = byteSource.asCharSource(Charsets.UTF_8).read();
assertThat(text, equalTo(originalString));
}
执行流程:
- 将 InputStream 包装为 ByteSource
- 将 ByteSource 视为 UTF8 编码的 CharSource
- 通过 CharSource 读取为字符串
✅ 更简单的 Guava 方案(需显式关闭流):
@Test
public void givenUsingGuavaAndJava7_whenConvertingAnInputStreamToAString_thenCorrect()
throws IOException {
String originalString = randomAlphabetic(8);
InputStream inputStream = new ByteArrayInputStream(originalString.getBytes());
String text = null;
try (Reader reader = new InputStreamReader(inputStream)) {
text = CharStreams.toString(reader);
}
assertThat(text, equalTo(originalString));
}
9. 使用 Apache Commons IO 转换
最后看 Commons IO 的实现方案:
⚠️ 注意: 与 Guava 不同,以下两种方式都不会自动关闭 InputStream
@Test
public void givenUsingCommonsIo_whenConvertingAnInputStreamToAString_thenCorrect()
throws IOException {
String originalString = randomAlphabetic(8);
InputStream inputStream = new ByteArrayInputStream(originalString.getBytes());
String text = IOUtils.toString(inputStream, StandardCharsets.UTF_8.name());
assertThat(text, equalTo(originalString));
}
也可使用 StringWriter 方式:
@Test
public void givenUsingCommonsIoWithCopy_whenConvertingAnInputStreamToAString_thenCorrect()
throws IOException {
String originalString = randomAlphabetic(8);
InputStream inputStream = new ByteArrayInputStream(originalString.getBytes());
StringWriter writer = new StringWriter();
String encoding = StandardCharsets.UTF_8.name();
IOUtils.copy(inputStream, writer, encoding);
assertThat(writer.toString(), equalTo(originalString));
}
10. 总结
本文介绍了将 InputStream 转换为字符串 的多种方法:
- 纯 Java 方案(包括 Java 8/9 新特性)
- 使用 Guava 库
- 使用 Apache Commons IO 库
所有示例代码均可在 GitHub 获取完整实现。
💡 选择建议:
- Java 9+ 优先用
readAllBytes()
(小文件场景)- Java 8 用
BufferedReader.lines()
- 需要兼容旧版本或处理大文件时,考虑分块读取或第三方库