应用场景 第三方调用的我的接口,上传若干个文件,我用 MultiPartFile[]
数组接收,之后我调用其他服务的接口,把文件发送过去,统一保存
目前存在的问题就是,当你使用 feign
传递 MultipartFile
对象时,接收方无法解析,所以需要重写 feign
的 Encoder
,让它支持 MultipartFile
类型以及 MultipartFile[]
数组类型
异常输出 1 com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class java.io.FileDescriptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile[0]->org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile["inputStream"]->java.io.FileInputStream["fd"])
Maven引入jar包 1 2 3 4 5 6 7 8 9 10 11 <dependency > <groupId > io.github.openfeign.form</groupId > <artifactId > feign-form</artifactId > <version > 3.3.0</version > </dependency > <dependency > <groupId > io.github.openfeign.form</groupId > <artifactId > feign-form-spring</artifactId > <version > 3.3.0</version > </dependency >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 import feign.RequestTemplate;import feign.codec.EncodeException;import feign.codec.Encoder;import feign.form.ContentType;import feign.form.FormEncoder;import feign.form.MultipartFormContentProcessor;import feign.form.spring.SpringManyMultipartFilesWriter;import feign.form.spring.SpringSingleMultipartFileWriter;import org.springframework.web.multipart.MultipartFile;import java.lang.reflect.Type;import java.util.Collections;import java.util.Map;public class SpringMultipartEncoder extends FormEncoder { public SpringMultipartEncoder () { this (new Default ()); } public SpringMultipartEncoder (Encoder delegate) { super (delegate); MultipartFormContentProcessor processor = (MultipartFormContentProcessor) getContentProcessor(ContentType.MULTIPART); processor.addWriter(new SpringSingleMultipartFileWriter ()); processor.addWriter(new SpringManyMultipartFilesWriter ()); } @Override public void encode (Object object, Type bodyType, RequestTemplate template) throws EncodeException { if (bodyType.equals(MultipartFile.class)) { MultipartFile file = (MultipartFile) object; Map data = Collections.singletonMap(file.getName(), object); super .encode(data, MAP_STRING_WILDCARD, template); return ; } else if (bodyType.equals(MultipartFile[].class)) { MultipartFile[] file = (MultipartFile[]) object; if (file != null ) { Map data = Collections.singletonMap(file.length == 0 ? "" : file[0 ].getName(), object); super .encode(data, MAP_STRING_WILDCARD, template); return ; } } super .encode(object, bodyType, template); } }
FeignMultipartSupportConfig
类 - 配置 Encoder
注入 messageConverters
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import feign.codec.Encoder;import org.springframework.beans.factory.ObjectFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.autoconfigure.http.HttpMessageConverters;import org.springframework.cloud.openfeign.support.SpringEncoder;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configuration public class FeignMultipartSupportConfig { @Autowired private ObjectFactory<HttpMessageConverters> messageConverters; @Bean public Encoder feignEncoder () { return new SpringMultipartEncoder (new SpringEncoder (messageConverters)); } }
消费者的文件服务客户端 FileClient
类 - FeignClient 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @FeignClient(name = "file-service", configuration = SpringMultipartEncoder.class) public interface FileClient { @PostMapping(value = "/api/file/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) List<String> upload (@RequestPart(value = "files") MultipartFile[] files, @RequestParam("firstKey") String firstKey) ; }
1 Caused by: org.apache.commons.fileupload.FileUploadException: the request was rejected because no multipart boundary was found
@RequestParam
适用于 name-valueString
类型的请求域
@RequestPart
适用于复杂的请求域 (JSON,XML)
消费者的文件服务控制层 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Autowired private FileClient fileClient; @PostMapping("file/v1/upload") public List<String> fileUpload (@RequestParam("files") MultipartFile[] files, String firstKey) { List<String> strings = fileClient.upload(files, firstKey); return strings; }
生产者的文件服务控制层 1 2 3 4 5 6 7 8 9 10 11 @PostMapping("/api/file/upload") public List<String> upload (@RequestParam("files") MultipartFile[] files, @RequestParam("firstKey") String firstKey) { return new LinkedList <>(); }