You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用RestTemplate上传文件报错Stream is closed求助

问题排查与解决

错误原因

你遇到的Stream is closed错误,核心原因是**MultipartInputStreamFileResource的设计限制**:它的getInputStream()方法直接返回传入的原始InputStream,而该流只能被读取一次。在Spring 5.1.6版本中,RestTemplate处理Multipart请求时,可能会多次尝试读取流(比如先计算内容长度,再实际发送请求),第一次读取后流被关闭,第二次读取就会抛出流已关闭的异常。

解决方案

针对这个问题,有两种可行的解决方式,根据你的文件大小选择:

方案1:转为ByteArrayResource(适合小文件)

将InputStream转为字节数组,用ByteArrayResource包装,它的getInputStream()每次都会返回新的流实例,避免流被关闭后无法复用的问题。

示例代码:

// 先将InputStream转为字节数组(可使用Apache Commons IO或自行实现)
byte[] fileBytes = IOUtils.toByteArray(data); // 需引入org.apache.commons:commons-io依赖

LinkedMultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
map.add("data", new ByteArrayResource(fileBytes) {
    @Override
    public String getFilename() {
        return fileName; // 重写getFilename方法返回文件名
    }
});

// 后续请求代码不变
HttpEntity<LinkedMultiValueMap<String, Object>> requestEntity = new HttpEntity<>(map, headers);
return getRestTemplate().postForObject("https://external-service/", requestEntity, MyResponse.class);

如果不想引入第三方依赖,可自行实现InputStream转字节数组:

private byte[] readInputStreamToBytes(InputStream inputStream) throws IOException {
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    byte[] buffer = new byte[1024];
    int length;
    while ((length = inputStream.read(buffer)) != -1) {
        outputStream.write(buffer, 0, length);
    }
    return outputStream.toByteArray();
}

方案2:自定义可重复读取的Resource(适合大文件)

如果文件过大,转字节数组会占用过多内存,可以自定义一个Resource类,每次调用getInputStream()时重新打开流。比如如果你的InputStream来自文件,可实现如下:

public class ReusableFileResource extends AbstractResource {
    private final File file;
    private final String fileName;

    public ReusableFileResource(File file, String fileName) {
        this.file = file;
        this.fileName = fileName;
    }

    @Override
    public InputStream getInputStream() throws IOException {
        return new FileInputStream(file); // 每次返回新的流
    }

    @Override
    public String getFilename() {
        return fileName;
    }

    @Override
    public String getDescription() {
        return "Reusable file resource for " + file.getAbsolutePath();
    }
}

使用时替换原来的MultipartInputStreamFileResource:

// 假设你的data来自File,先获取对应的File对象
map.add("data", new ReusableFileResource(targetFile, fileName));

额外注意事项

  • 确保你的InputStream来源支持重新打开(比如文件流可以,而网络流可能不行),如果是一次性的流(如请求体流),只能用方案1转为字节数组。
  • 检查RestTemplate的配置,避免添加会提前读取或关闭流的拦截器。

内容的提问来源于stack exchange,提问作者Adelin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 11:00:14