spring cloud實戰與思考(二) 微服務之間通過fiegn上傳多個文件1
需求場景:
- 微服務之間調用接口一次性上傳多個文件。
- 上傳文件的同時附帶其他參數。
- 多個文件能有效的區分開,以便進行不同處理。
Spring cloud的微服務之間接口調用使用Feign。原裝的Feign不支持文件的傳輸。需要借助“Feign-form”庫才行。但是貌似“Feign-form”庫(至少是3.0.3版本)只支持單文件上傳。在接口中使用多文件參數時會報異常:
feign.codec.EncodeException: class [Lorg.springframework.web.multipart.MultipartFile; is not a type supported by thisencoder. at feign.codec.Encoder$Default.encode(Encoder.java:90) ~[feign-core-9.5.0.jar:na] at feign.form.FormEncoder.encode(FormEncoder.java:87) ~[feign-form-3.0.3.jar:3.0.3] at feign.form.spring.SpringFormEncoder.encode(SpringFormEncoder.java:62) ~[feign-form-spring-3.0.3.jar:3.0.3] at feign.ReflectiveFeign$BuildEncodedTemplateFromArgs.resolve(ReflectiveFeign.java:351) ~[feign-core-9.5.0.jar:na] at feign.ReflectiveFeign$BuildTemplateByResolvingArgs.create(ReflectiveFeign.java:213) ~[feign-core-9.5.0.jar:na] at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:72) ~[feign-core-9.5.0.jar:na] at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:103) ~[feign-core-9.5.0.jar:na] at com.sun.proxy.$Proxy96.insertWithFiles(Unknown Source) ~[na:na]
在網上搜索一番後,參考博客“https://blog.csdn.net/ytzzh0726/article/details/79467843”,將”Feign-form”庫中的”SpringFormEncoder”類改動一下,就可以支持多文件的上傳。下面是具體實現方法:
微服務提供方Controller接口:
@ResponseBody @RequestMapping(value="/psts/add/insertWithFiles", method = RequestMethod.POST) public Object insertWithFiles(@RequestParam("baseInfo") String baseInfo, @RequestPart(value = "files") MultipartFile[] photoFiles) { }
服務消費方pom引用Feign-form依賴:
<dependency> <groupId>io.github.openfeign.form</groupId> <artifactId>feign-form</artifactId> <version>3.0.3</version> </dependency> <dependency> <groupId>io.github.openfeign.form</groupId> <artifactId>feign-form-spring</artifactId> <version>3.0.3</version> </dependency> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.3</version> </dependency>
服務消費方聲明一個“FeignSpringFormEncoder”類(這個類copy自”SpringFormEncoder”接口):
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 FeignSpringFormEncoder extends FormEncoder { public FeignSpringFormEncoder() { this(new Default()); } public FeignSpringFormEncoder(Encoder delegate) { super(delegate); MultipartFormContentProcessor processor = (MultipartFormContentProcessor)this.getContentProcessor(ContentType.MULTIPART); processor.addWriter(new SpringSingleMultipartFileWriter()); processor.addWriter(new SpringManyMultipartFilesWriter()); } public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException { //註釋掉原來的代碼 // if (!bodyType.equals(MultipartFile.class)) { // super.encode(object, bodyType, template); // } else { // MultipartFile file = (MultipartFile)object; // Map<String, Object> data = Collections.singletonMap(file.getName(), object); // super.encode(data, MAP_STRING_WILDCARD, template); // } //修改為下面的代碼 if (bodyType.equals(MultipartFile.class)) { MultipartFile file = (MultipartFile) object; Map<String, Object> 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<String, Object> data = Collections.singletonMap(“files”, object); super.encode(data, MAP_STRING_WILDCARD, template); return; } } super.encode(object, bodyType, template); } }
將“FeignSpringFormEncoder”作為bean提供給框架,代替“SpringFormEncoder”的“encode()”接口:
@Configuration public class FeignMultipartSupportConfig { @Bean @Primary @Scope("prototype") public Encoder multipartFormEncoder() { // return new SpringFormEncoder(); return new FeignSpringFormEncoder(); } @Bean public feign.Logger.Level multipartLoggerLevel() { return feign.Logger.Level.FULL; } }
以上方案測試可行。到目前為止需求1“多文件上傳”和需求2“非文件類型參數上傳”都已經滿足了。下面來看看怎麽對文件數組中的文件進行區分。服務提供方接收到的“MultipartFile”有兩個接口“getName()”和“getOriginalFilename()”分別對應文件在http頭的“Metadata”名稱和文件原始名稱。因為使用文件數組上傳的功能,前一個名稱被固定為“files”,不能用於區分文件。看來只能通過對文件原始名稱進行約定來區分文件。但是如果這些文件是用戶上傳的,這就要求用戶上傳文件前對文件名稱按照約定修改。顯然這種接口方式對用戶很不友好。限於篇幅,下一篇微博來探討一下這個問題的解決方法。
spring cloud實戰與思考(二) 微服務之間通過fiegn上傳多個文件1