Contents

将 InputStream转换为 String

1. 概述

在本教程中,我们将了解如何将InputStream转换为 String。

我们将从使用普通 Java 开始,包括 Java8/9 解决方案,然后研究如何使用GuavaApache Commons IO 库。

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, Charset.forName(StandardCharsets.UTF_8.name())))) {
        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()*方法。readLine()假定一行以换行符 (“\n”)、回车符 (“\r”) 或回车符后紧跟换行符中的任何一种来终止。换句话说,它支持所有常见的End Of Line样式:Unix、Windows 甚至旧的 Mac OS。

另一方面,当我们使用Collectors.joining()时,我们需要明确决定要为创建的String使用哪种类型的 EOL 。

我们还可以使用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));
}

请注意,InputStream将在Scanner关闭时关闭。

还值得澄清一下useDelimiter("\A") 的作用。这里我们传递了’\A’,它是一个边界标记正则表达式,表示输入的开始。本质上,这意味着*next()*调用读取整个输入流。

这是 Java 7 示例而不是 Java 5 示例的唯一原因是使用了try-with-resources语句。如果我们把它变成一个标准的try-finally块,它可以用 Java 5 编译得很好。

6. 使用ByteArrayOutputStream 进行转换

最后,让我们看另一个普通的 Java 示例,这次使用的是ByteArrayOutputStream类:

@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转换为字节数组,用于创建String

7. 使用java.nio 进行转换

另一种解决方案是将InputStream的内容复制到文件中,然后将其转换为String:**

@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的内容复制到文件中。然后使用相同的类通过readAllBytes()方法将文件内容转换为String

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,但需要显式关闭流;幸运的是,我们可以简单地使用 try-with-resources 语法来解决这个问题:

@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));
}