使用Feign Client将InputStream转为MultipartFile并实现流式传输
解决方案核心思路
- 从原始
Part中获取文件元数据(大小、文件名)——Part本身提供了getSize()和getSubmittedFileName()方法,无需仅保存InputStream,直接拿到这些信息就能解决文件大小缺失的问题。 - 自定义
MultipartFile实现类,基于InputStream和元数据构建,全程不将文件内容加载到内存。 - 配置Feign客户端支持流式multipart上传,确保请求过程中无内存缓存。
步骤1:修正文件元数据存储
先修改FileDto,添加文件大小和文件名字段,读取Part时一并保存:
// 修正后的FileDto public class FileDto { private InputStream fileContent; private long fileSize; private String fileName; private String contentType; // getter、setter省略 } // 读取Part时的代码修改 servletRequest.getParts().forEach(part -> { // 跳过body部件,仅处理文件类型的部件 if (!"body".equals(part.getName())) { FileDto fileDto = new FileDto(); fileDto.setFileContent(part.getInputStream()); fileDto.setFileSize(part.getSize()); // 直接从Part获取文件大小 fileDto.setFileName(part.getSubmittedFileName()); // 获取原始文件名 fileDto.setContentType(part.getContentType()); // 获取文件MIME类型 // 将fileDto存入业务逻辑 } });
步骤2:自定义流式MultipartFile实现
实现MultipartFile接口,完全基于传入的流和元数据,避免内存加载:
import org.springframework.web.multipart.MultipartFile; import java.io.*; public class StreamMultipartFile implements MultipartFile { private final InputStream inputStream; private final long size; private final String fileName; private final String contentType; public StreamMultipartFile(InputStream inputStream, long size, String fileName, String contentType) { this.inputStream = inputStream; this.size = size; this.fileName = fileName; this.contentType = contentType; } @Override public String getName() { return "file"; // 对应Feign接口中@RequestPart("file")的参数名 } @Override public String getOriginalFilename() { return fileName; } @Override public String getContentType() { return contentType; } @Override public boolean isEmpty() { return size == 0; } @Override public long getSize() { return size; } @Override public byte[] getBytes() throws IOException { // 禁止此方法调用,避免触发内存加载 throw new UnsupportedOperationException("getBytes() 不支持流式MultipartFile"); } @Override public InputStream getInputStream() throws IOException { return inputStream; } @Override public void transferTo(File dest) throws IOException, IllegalStateException { // 可选实现:如需转存到本地文件可启用,本次场景无需 try (InputStream is = inputStream; OutputStream os = new FileOutputStream(dest)) { is.transferTo(os); } } }
对应的转换方法实现:
private MultipartFile convertToMultiPartFile(FileDto fileDto) { return new StreamMultipartFile( fileDto.getFileContent(), fileDto.getFileSize(), fileDto.getFileName(), fileDto.getContentType() ); }
步骤3:配置Feign客户端支持流式上传
首先添加依赖(以Apache HttpClient为例):
<!-- pom.xml --> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> </dependency> <dependency> <groupId>io.github.openfeign.form</groupId> <artifactId>feign-form</artifactId> <version>3.8.0</version> <!-- 适配你的Spring Cloud版本 --> </dependency> <dependency> <groupId>io.github.openfeign.form</groupId> <artifactId>feign-form-spring</artifactId> <version>3.8.0</version> </dependency>
然后配置Feign的编码器和HTTP客户端:
import feign.codec.Encoder; import feign.form.spring.SpringFormEncoder; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class FeignConfig { @Bean public Encoder feignEncoder() { // 使用SpringFormEncoder支持multipart请求,确保流式处理 return new SpringFormEncoder(); } // 配置Apache HttpClient禁用内容缓存,支持流式上传 @Bean public CloseableHttpClient httpClient() { return HttpClientBuilder.create() .disableContentCompression() .disableCookieManagement() .build(); } }
步骤4:简化Feign接口注解
去掉冗余的headers和produces配置,@RequestPart会自动处理Content-Type:
@FeignClient(name = "feignClient", url = "${api.url}", configuration = FeignConfig.class) public interface ExternalSystemConnector { @PostMapping(value = "/v1/file", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) void createFile(@RequestPart("body") AdditionalInfoDto requestDto, @RequestPart("file") MultipartFile file); }
关键注意事项
- 确保
InputStream仅在Feign调用时被读取:Part的输入流只能读取一次,不要在业务逻辑中提前消费流。 - 禁止调用
StreamMultipartFile的getBytes()方法:该方法已被禁用,防止误操作导致文件加载到内存。 - 必须替换默认HTTP客户端:默认的URLConnection会缓存请求内容到内存,使用Apache HttpClient或OkHttp才能实现真正的流式上传。
内容的提问来源于stack exchange,提问作者Zis
相关产品推荐
相关产品推荐

