使用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
相关产品推荐
相关产品推荐

