使用Feign擴(kuò)展包實(shí)現(xiàn)微服務(wù)間文件上傳
在Spring Cloud 的Feign組件中并不支持文件的傳輸,會(huì)出現(xiàn)這樣的錯(cuò)誤提示:
feign.codec.EncodeException: class [Lorg.springframework.web.multipart.MultipartFile; is not a type supported by this encoder. at feign.codec.Encoder$Default.encode(Encoder.java:90) ~[feign-core-9.5.1.jar:na] at feign.form.FormEncoder.encode(FormEncoder.java:87) ~[feign-form-3.3.0.jar:3.3.0] at feign.form.spring.SpringFormEncoder.encode(SpringFormEncoder.java:64) ~[feign-form-spring-3.3.0.jar:3.3.0]
但是我們可以通過(guò)使用Feign的擴(kuò)展包實(shí)現(xiàn)這個(gè)功能。
一. 示例介紹
我們調(diào)用feign_upload_second的上傳文件接口上傳文件,feign_upload_second內(nèi)部使用feign調(diào)用feign_upload_first實(shí)現(xiàn)文件上傳。
二 、單文件上傳
2.1 feign_upload_first服務(wù)提供者
文件上傳的服務(wù)提供者接口比較簡(jiǎn)單,如下所示:
@SpringBootApplication public class FeignUploadFirstApplication { @RestController public class UploadController { @RequestMapping(value = "/uploadFile",method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public String handleFileUpload(@RequestPart(value = "file") MultipartFile file) { return file.getOriginalFilename(); } } public static void main(String[] args) { SpringApplication.run(FeignUploadFirstApplication.class, args); } }
2.2 feign_upload_second服務(wù)消費(fèi)者
增加擴(kuò)展包依賴
<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> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.3</version> </dependency>
新增feign實(shí)現(xiàn)文件上傳的配置類
@Configuration public class FeignSupportConfig { @Bean public Encoder feignFormEncoder() { return new SpringFormEncoder(); } }
feign遠(yuǎn)程調(diào)用接口
@FeignClient(name = "file",url = "http://localhost:8100",configuration = FeignSupportConfig.class) public interface UploadService { @RequestMapping(value = "/uploadFile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) String handleFileUpload(@RequestPart(value = "file") MultipartFile file); }
上傳文件接口
@RestController public class UploadController { @Autowired UploadService uploadService; @RequestMapping(value = "/uploadFile",method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public String handleFileUpload(@RequestPart(value = "file") MultipartFile file) { return uploadService.handleFileUpload(file); } }
2.3 測(cè)試
使用postman進(jìn)行測(cè)試,可以正常上傳文件
三、多文件上傳
既然單個(gè)文件可以上傳,那么多文件應(yīng)該也沒(méi)問(wèn)題吧,我們對(duì)上面的代碼進(jìn)行修改
3.1 feign_upload_first服務(wù)提供者
文件上傳的服務(wù)提供者接口比較簡(jiǎn)單,如下所示:
@SpringBootApplication public class FeignUploadFirstApplication { @RestController public class UploadController { @RequestMapping(value = "/uploadFile",method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public String handleFileUpload(@RequestPart(value = "file") MultipartFile file) { return file.getOriginalFilename(); } @RequestMapping(value = "/uploadFile2",method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public String handleFileUpload(@RequestPart(value = "file") MultipartFile[] file) { String fileName = ""; for(MultipartFile f : file){ fileName += f.getOriginalFilename()+"---"; } return fileName; } } public static void main(String[] args) { SpringApplication.run(FeignUploadFirstApplication.class, args); } }
3.2 feign_upload_second服務(wù)消費(fèi)者
feign遠(yuǎn)程調(diào)用接口
@FeignClient(name = "file",url = "http://localhost:8100",configuration = FeignSupportConfig.class) public interface UploadService { @RequestMapping(value = "/uploadFile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) String handleFileUpload(@RequestPart(value = "file") MultipartFile file); @RequestMapping(value = "/uploadFile2", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) String handleFileUpload(@RequestPart(value = "file") MultipartFile[] file); }
上傳文件接口
@RestController public class UploadController { @Autowired UploadService uploadService; @RequestMapping(value = "/uploadFile",method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public String handleFileUpload(@RequestPart(value = "file") MultipartFile file) { return uploadService.handleFileUpload(file); } @RequestMapping(value = "/uploadFile2",method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public String handleFileUpload2(@RequestPart(value = "file") MultipartFile[] file) { return uploadService.handleFileUpload(file); } }
3.3 測(cè)試
經(jīng)過(guò)測(cè)試發(fā)現(xiàn),無(wú)法上傳多個(gè)文件。經(jīng)過(guò)檢查,發(fā)現(xiàn)源碼里底層是有對(duì)MultipartFile[]類型的支持的,源碼中有個(gè)類叫SpringManyMultipartFilesWriter,是專門針對(duì)文件數(shù)組類型進(jìn)行操作的,但是配置到項(xiàng)目里的SpringFormEncoder類里卻沒(méi)有對(duì)文件數(shù)組類型的判斷,以致不能支持文件數(shù)組的上傳
SpringManyMultipartFilesWriter源碼
public class SpringManyMultipartFilesWriter extends AbstractWriter { private final SpringSingleMultipartFileWriter fileWriter = new SpringSingleMultipartFileWriter(); public SpringManyMultipartFilesWriter() { } public void write(Output output, String boundary, String key, Object value) throws Exception { if (value instanceof MultipartFile[]) { MultipartFile[] files = (MultipartFile[])((MultipartFile[])value); MultipartFile[] var6 = files; int var7 = files.length; for(int var8 = 0; var8 < var7; ++var8) { MultipartFile file = var6[var8]; this.fileWriter.write(output, boundary, key, file); } } else if (value instanceof Iterable) { Iterable<?> iterable = (Iterable)value; Iterator var11 = iterable.iterator(); while(var11.hasNext()) { Object file = var11.next(); this.fileWriter.write(output, boundary, key, file); } } } public boolean isApplicable(Object value) { if (value == null) { return false; } else if (value instanceof MultipartFile[]) { return true; } else { if (value instanceof Iterable) { Iterable<?> iterable = (Iterable)value; Iterator<?> iterator = iterable.iterator(); if (iterator.hasNext() && iterator.next() instanceof MultipartFile) { return true; } } return false; } } }
SpringFormEncoder源碼
public class SpringFormEncoder extends FormEncoder { public SpringFormEncoder() { this(new Default()); } public SpringFormEncoder(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); } } }
從上面SpringFormEncoder的源碼上可以看到SpringFormEncoder類構(gòu)造時(shí)把SpringManyMultipartFilesWriter實(shí)例添加到了處理器列表里了,但是在encode方法里又只判斷了MultipartFile類型,沒(méi)有判斷數(shù)組類型,底層有對(duì)數(shù)組的支持但上層卻缺少了相應(yīng)判斷。那么我們可以自己去擴(kuò)展FormEncoder,仿照SpringFormEncoder源碼,只修改encode方法。
3.3 擴(kuò)展FormEncoder支持多文件上傳
擴(kuò)展FormEncoder,命名為FeignSpringFormEncoder
public class FeignSpringFormEncoder extends FormEncoder { /** * Constructor with the default Feign's encoder as a delegate. */ public FeignSpringFormEncoder() { this(new Default()); } /** * Constructor with specified delegate encoder. * * @param delegate delegate encoder, if this encoder couldn't encode object. */ public FeignSpringFormEncoder(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); } }
注冊(cè)配置類
@Configuration public class FeignSupportConfig { @Bean public Encoder feignFormEncoder() { return new FeignSpringFormEncoder(); } }
經(jīng)過(guò)測(cè)試可以上傳多個(gè)文件。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Java JDBC連接數(shù)據(jù)庫(kù)常見(jiàn)操作總結(jié)
這篇文章主要介紹了Java JDBC連接數(shù)據(jù)庫(kù)常見(jiàn)操作,結(jié)合實(shí)例形式總結(jié)分析了java基于jdbc連接mysql、Oracle數(shù)據(jù)庫(kù)及連接池相關(guān)操作技巧,需要的朋友可以參考下2019-03-03SpringCloudStream原理和深入使用小結(jié)
Spring?Cloud?Stream是一個(gè)用于構(gòu)建與共享消息傳遞系統(tǒng)連接的高度可擴(kuò)展的事件驅(qū)動(dòng)型微服務(wù)的框架,本文給大家介紹SpringCloudStream原理和深入使用,感興趣的朋友跟隨小編一起看看吧2024-06-06SpringBoot深入理解之內(nèi)置web容器及配置的總結(jié)
今天小編就為大家分享一篇關(guān)于SpringBoot深入理解之內(nèi)置web容器及配置的總結(jié),小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-03-03一篇超詳細(xì)的Spring Boot整合Mybatis文章
大家都知道springboot搭建一個(gè)spring框架只需要秒秒鐘。下面通過(guò)實(shí)例代碼給大家介紹一下springboot與mybatis的完美融合,非常不錯(cuò),具有參考借鑒價(jià)值,感興趣的朋友一起看看吧2021-07-07Java利用位運(yùn)算實(shí)現(xiàn)乘法運(yùn)算詳解
這篇文章主要為大家詳細(xì)介紹了Java如何用位運(yùn)算實(shí)現(xiàn)乘法運(yùn)算,在實(shí)現(xiàn)乘法時(shí)要用位運(yùn)算實(shí)現(xiàn),并且不能出現(xiàn)加減乘除任何符號(hào),感興趣的可以了解一下2023-04-04java中Sources目錄Resources目錄的區(qū)別解讀
這篇文章主要介紹了java中Sources目錄Resources目錄的區(qū)別解讀,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12Java連接Mysql數(shù)據(jù)庫(kù)詳細(xì)代碼實(shí)例
這篇文章主要介紹了Java連接Mysql數(shù)據(jù)庫(kù)詳細(xì)代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02SpringBoot項(xiàng)目使用協(xié)同過(guò)濾的實(shí)現(xiàn)
協(xié)同過(guò)濾是一種常用的推薦系統(tǒng)算法,用于預(yù)測(cè)用戶可能喜歡的物品,本文主要介紹了SpringBoot項(xiàng)目使用協(xié)同過(guò)濾的實(shí)現(xiàn),感興趣的可以了解一下2023-09-09