使用Feign扩展包实现微服务间文件上传
在spring cloud 的feign组件中并不支持文件的传输,会出现这样的错误提示:
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]
但是我们可以通过使用feign的扩展包实现这个功能。
一. 示例介绍
我们调用feign_upload_second的上传文件接口上传文件,feign_upload_second内部使用feign调用feign_upload_first实现文件上传。
二 、单文件上传
2.1 feign_upload_first服务提供者
文件上传的服务提供者接口比较简单,如下所示:
@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服务消费者
增加扩展包依赖
<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实现文件上传的配置类
@configuration public class feignsupportconfig { @bean public encoder feignformencoder() { return new springformencoder(); } }
feign远程调用接口
@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 测试
使用postman进行测试,可以正常上传文件
三、多文件上传
既然单个文件可以上传,那么多文件应该也没问题吧,我们对上面的代码进行修改
3.1 feign_upload_first服务提供者
文件上传的服务提供者接口比较简单,如下所示:
@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服务消费者
feign远程调用接口
@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 测试
经过测试发现,无法上传多个文件。经过检查,发现源码里底层是有对multipartfile[]类型的支持的,源码中有个类叫springmanymultipartfileswriter,是专门针对文件数组类型进行操作的,但是配置到项目里的springformencoder类里却没有对文件数组类型的判断,以致不能支持文件数组的上传
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类构造时把springmanymultipartfileswriter实例添加到了处理器列表里了,但是在encode方法里又只判断了multipartfile类型,没有判断数组类型,底层有对数组的支持但上层却缺少了相应判断。那么我们可以自己去扩展formencoder,仿照springformencoder源码,只修改encode方法。
3.3 扩展formencoder支持多文件上传
扩展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); } }
注册配置类
@configuration public class feignsupportconfig { @bean public encoder feignformencoder() { return new feignspringformencoder(); } }
经过测试可以上传多个文件。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。