1. 概述

本文将探讨 如何将 InputStream 转换为字符串。我们将从纯 Java 方案开始(包括 Java 8/9 的解决方案),然后介绍如何使用 GuavaApache 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));
}

核心流程:

  1. 通过分块读写将 InputStream 转为 ByteArrayOutputStream
  2. 将 OutputStream 转为 byte 数组
  3. 用 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));
}

实现步骤:

  1. 使用 java.nio.file.Files 创建临时文件
  2. 将 InputStream 内容复制到文件
  3. 通过 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));
}

执行流程:

  1. 将 InputStream 包装为 ByteSource
  2. 将 ByteSource 视为 UTF8 编码的 CharSource
  3. 通过 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 转换为字符串 的多种方法:

所有示例代码均可在 GitHub 获取完整实现。

💡 选择建议:

  • Java 9+ 优先用 readAllBytes()(小文件场景)
  • Java 8 用 BufferedReader.lines()
  • 需要兼容旧版本或处理大文件时,考虑分块读取或第三方库

原始标题:Java InputStream to String