1. 概述

本文将深入探讨 Spring 的 StreamUtils 工具类及其使用方法。简单来说,StreamUtils 是 Spring 提供的一个实用工具类,专门用于处理 java.io 包中的 InputStreamOutputStream 流操作。⚠️ 注意:它和 Java 8 的 Stream API 没有任何关系。

2. Maven 依赖

StreamUtils 位于 spring-core 模块中,添加以下依赖到 pom.xml

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>5.2.8.RELEASE</version>
</dependency>

最新版本可在 Maven 中央仓库 查找。

3. 流复制操作

StreamUtils 提供了多个重载的 copy() 方法及其变体:

  • copyRange():复制指定范围内容
  • copyToByteArray():复制到字节数组
  • copyString():复制字符串内容

虽然不用库也能实现流复制,但代码会非常繁琐且难以维护。注意:为简化示例,以下代码省略了流关闭操作

3.1 输入流复制到输出流

@Test
public void whenCopyInputStreamToOutputStream_thenCorrect() throws IOException {
    String inputFileName = "src/test/resources/input.txt";
    String outputFileName = "src/test/resources/output.txt";
    File outputFile = new File(outputFileName);
    InputStream in = new FileInputStream(inputFileName);
    OutputStream out = new FileOutputStream(outputFile);
    
    StreamUtils.copy(in, out);
    
    assertTrue(outputFile.exists());
    String inputFileContent = getStringFromInputStream(new FileInputStream(inputFileName));
    String outputFileContent = getStringFromInputStream(new FileInputStream(outputFileName));
    assertEquals(inputFileContent, outputFileContent);
}

✅ 执行后输出文件将包含输入流的全部内容。

3.2 复制指定范围内容

使用 copyRange() 复制输入流的指定范围到输出流:

@Test
public void whenCopyRangeOfInputStreamToOutputStream_thenCorrect() throws IOException {
    String inputFileName = "src/test/resources/input.txt";
    String outputFileName = "src/test/resources/output.txt";
    File outputFile = new File(outputFileName);
    InputStream in = new FileInputStream(inputFileName);
    OutputStream out = new FileOutputStream(outputFileName);
    
    StreamUtils.copyRange(in, out, 1, 10);
    
    assertTrue(outputFile.exists());
    String inputFileContent = getStringFromInputStream(new FileInputStream(inputFileName));
    String outputFileContent = getStringFromInputStream(new FileInputStream(outputFileName));
 
    assertEquals(inputFileContent.substring(1, 11), outputFileContent);
}

⚠️ 如果指定范围超出输入流长度,方法会自动复制到流末尾。

3.3 字符串复制到输出流

@Test
public void whenCopyStringToOutputStream_thenCorrect() throws IOException {
    String string = "Should be copied to OutputStream.";
    String outputFileName = "src/test/resources/output.txt";
    File outputFile = new File(outputFileName);
    OutputStream out = new FileOutputStream("src/test/resources/output.txt");
    
    StreamUtils.copy(string, StandardCharsets.UTF_8, out);
    
    assertTrue(outputFile.exists());
 
    String outputFileContent = getStringFromInputStream(new FileInputStream(outputFileName));
 
    assertEquals(outputFileContent, string);
}

参数说明:

  1. 待复制的字符串
  2. 字符编码(如 StandardCharsets.UTF_8
  3. 目标输出流

3.4 输入流转换为字符串

@Test
public void whenCopyInputStreamToString_thenCorrect() throws IOException {
    String inputFileName = "src/test/resources/input.txt";
    InputStream is = new FileInputStream(inputFileName);
    String content = StreamUtils.copyToString(is, StandardCharsets.UTF_8);
    
    String inputFileContent = getStringFromInputStream(new FileInputStream(inputFileName));
    assertEquals(inputFileContent, content);
}

3.5 字节数组与流互转

字节数组 → 输出流

public void whenCopyByteArrayToOutputStream_thenCorrect() throws IOException {
    String outputFileName = "src/test/resources/output.txt";
    String string = "Should be copied to OutputStream.";
    byte[] byteArray = string.getBytes();
    OutputStream out = new FileOutputStream("src/test/resources/output.txt");
    
    StreamUtils.copy(byteArray, out);
    
    String outputFileContent = getStringFromInputStream(new FileInputStream(outputFileName));
 
    assertEquals(outputFileContent, string);
}

输入流 → 字节数组

public void whenCopyInputStreamToByteArray_thenCorrect() throws IOException {
    String inputFileName = "src/test/resources/input.txt";
    InputStream is = new FileInputStream(inputFileName);
    byte[] out = StreamUtils.copyToByteArray(is);
    
    String content = new String(out);
    String inputFileContent = getStringFromInputStream(new FileInputStream(inputFileName));
 
    assertEquals(inputFileContent, content);
}

4. 其他实用功能

4.1 清空流数据

使用 drain() 方法消耗输入流中的剩余数据:

StreamUtils.drain(in);

4.2 获取空输入流

通过 emptyInput() 获取高效的空输入流:

public InputStream getInputStream() {
    return StreamUtils.emptyInput();
}

4.3 非关闭流包装

使用 nonClosing() 包装流,使其忽略 close() 调用:

public InputStream getNonClosingInputStream() throws IOException {
    InputStream in = new FileInputStream("src/test/resources/input.txt");
    return StreamUtils.nonClosing(in);
}

✅ 适用于需要保持流开启的场景(如 HTTP 响应流)。

5. 总结

本文全面介绍了 StreamUtils 的核心功能:

  • 流复制操作(完整/范围/字符串/字节数组)
  • 特殊流处理(清空/空流/非关闭流)
  • 实际应用场景示例

完整代码示例可在 GitHub 仓库 查看。踩坑提醒:实际使用时务必记得关闭流,避免资源泄漏!


原始标题:Introduction to Spring's StreamUtils