如何在Spring Feign Client中通过@RequestPart传递多张图片?
问题描述
尝试使用Feign Client传递多个图片文件时,单个MultipartFile参数配合@RequestPart注解可以正常工作,但使用集合或数组类型的MultipartFile参数时会报错。
Feign Client方法代码:
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK", response = FileInfo.class) }) @PostMapping( value = "/upload-multi-files", produces = { "application/json" }, consumes = { "multipart/form-data" } ) ResponseEntity<FileInfo> uploadMultiFiles(@NotNull @Valid @RequestPart(value = "images", required = true) List<MultipartFile> images);
调用时抛出的错误:
feign.codec.EncodeException: Could not write request: no suitable HttpMessageConverter found for request type [java.util.ArrayList] and content type [multipart/form-data] at org.springframework.cloud.openfeign.support.SpringEncoder.encode(SpringEncoder.java:143) ~[spring-cloud-openfeign-core-2.1.0.RELEASE.jar:2.1.0.RELEASE] at feign.ReflectiveFeign$BuildEncodedTemplateFromArgs.resolve(ReflectiveFeign.java:372) ~[feign-core-10.1.0.jar:?] at feign.ReflectiveFeign$BuildTemplateByResolvingArgs.create(ReflectiveFeign.java:224) ~[feign-core-10.1.0.jar:?] ...
请问如何让该方法支持集合或数组类型的参数?
解决方案
方法一:自定义Feign Encoder扩展集合类型支持
Spring Cloud Feign默认的SpringEncoder不支持直接编码List<MultipartFile>类型,需要自定义Encoder处理集合类型的多文件参数:
- 创建自定义MultipartEncoder类:
import feign.RequestTemplate; import feign.codec.EncodeException; import org.springframework.cloud.openfeign.support.SpringEncoder; import org.springframework.core.io.InputStreamResource; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.multipart.MultipartFile; import org.springframework.beans.factory.ObjectFactory; import org.springframework.boot.autoconfigure.http.HttpMessageConverters; import java.lang.reflect.Type; import java.util.List; import java.util.Map; public class CustomMultipartEncoder extends SpringEncoder { public CustomMultipartEncoder(ObjectFactory<HttpMessageConverters> messageConverters) { super(messageConverters); } @Override public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException { if (object instanceof Map) { Map<String, Object> paramMap = (Map<String, Object>) object; boolean hasMultipartContent = paramMap.values().stream() .anyMatch(val -> val instanceof MultipartFile || val instanceof List); if (hasMultipartContent) { MultiValueMap<String, Object> multiValueMap = new LinkedMultiValueMap<>(); paramMap.forEach((key, val) -> { if (val instanceof List) { ((List<?>) val).forEach(item -> { if (item instanceof MultipartFile) { addMultipartFile(multiValueMap, key, (MultipartFile) item); } else { multiValueMap.add(key, item); } }); } else if (val instanceof MultipartFile) { addMultipartFile(multiValueMap, key, (MultipartFile) val); } else { multiValueMap.add(key, val); } }); super.encode(new HttpEntity<>(multiValueMap, getMultipartHeaders()), bodyType, template); return; } } super.encode(object, bodyType, template); } private void addMultipartFile(MultiValueMap<String, Object> map, String key, MultipartFile file) { HttpHeaders fileHeaders = new HttpHeaders(); fileHeaders.setContentType(MediaType.parseMediaType(file.getContentType())); fileHeaders.setContentDispositionFormData(key, file.getOriginalFilename()); map.add(key, new HttpEntity<>(new InputStreamResource(file.getInputStream()), fileHeaders)); } private HttpHeaders getMultipartHeaders() { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.MULTIPART_FORM_DATA); return headers; } }
- 配置Feign Client使用自定义Encoder:
import org.springframework.cloud.openfeign.FeignClient; import org.springframework.context.annotation.Bean; import org.springframework.boot.autoconfigure.http.HttpMessageConverters; import org.springframework.beans.factory.ObjectFactory; @FeignClient(name = "your-service-name", configuration = FileFeignConfig.class) public interface FileFeignClient { // 你的uploadMultiFiles方法 } class FileFeignConfig { @Bean public CustomMultipartEncoder feignEncoder() { return new CustomMultipartEncoder(new ObjectFactory<HttpMessageConverters>() { @Override public HttpMessageConverters getObject() { return new HttpMessageConverters(); } }); } }
方法二:简化参数写法(无需自定义Encoder)
如果不想自定义Encoder,可以调整参数形式:
- 改用数组类型参数:
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK", response = FileInfo.class) }) @PostMapping( value = "/upload-multi-files", produces = { "application/json" }, consumes = { "multipart/form-data" } ) ResponseEntity<FileInfo> uploadMultiFiles(@NotNull @Valid @RequestPart(value = "images", required = true) MultipartFile[] images);
调用时将List<MultipartFile>转换为数组传入即可。
- 或者将参数包装进DTO,使用
@ModelAttribute注解:
// 定义DTO public class MultiFileUploadDTO { private List<MultipartFile> images; // getter、setter } // Feign方法修改为 ResponseEntity<FileInfo> uploadMultiFiles(@ModelAttribute MultiFileUploadDTO uploadDTO);
这种方式需要服务端接口也对应调整为接收该DTO参数。
内容的提问来源于stack exchange,提问作者mar3g
相关产品推荐
相关产品推荐

