文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

微服务之间怎么通过feign调用接口上传文件

2023-06-20 12:48

关注

这篇文章主要介绍“微服务之间怎么通过feign调用接口上传文件”,在日常操作中,相信很多人在微服务之间怎么通过feign调用接口上传文件问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”微服务之间怎么通过feign调用接口上传文件”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

具体需求:

我们的项目是基于springboot框架的springcloud微服务搭建的,后端服务技术层面整体上分为business服务和core服务,business服务用于作为应用层,直接连接客户端,通常用于聚合数据,core服务用来客户端具体操作不同需求来控制数据库,文件上传是通过客户端上传接口,通过business服务,由服务端调用feign接口,也是第一次做这种文件中转,遇到各种问题,下面是我自己的解决方案,不喜勿喷,代码小白一枚;

一、core服务层接口@requestmapping

属性加上consumes=MediaType.MULTIPART_FORM_DATA_VALUE如下代码

@PostMapping(value = "/upload",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)    @ResponseBody    public Result<TbFile> upload(@RequestPart(value = "file",required = true) MultipartFile file,                         @RequestParam(name = "id",required = true) Integer id,                         @RequestParam(name = "desc",required = false)  String desc,                         @RequestParam(name = "fileId",required = false) Integer fileId )

解释:@RequestMapping存在以下两个属性:

String[] consumes() default {};

String[] produces() default {};

两个属性的解释及参考例子:

① 属性produces:指定返回值类型,并且可以设置返回值类型和返回值的字符编码;代码例子参考如下:

属性produces="application/json"时,返回json数据

属性produces="MediaType.APPLICATION_JSON_VALUE;charset=utf-8"时,设置返回数据的字符编码为utf-8

@Controller    @RequestMapping(value = "/getperson", method = RequestMethod.GET, produces="application/json")    public Object getPerson(int id) {           //实现自己的逻辑调用  Person p= new person();     return p;}

特别说明:produces="application/json"和注解@ResponseBody是一样的效果,使用了注解其实可以不使用该属性了

② 属性consumes: 指定处理请求当中的提交内容类型(Content-Type):application/json, text/html等;

代码例子参考如下:

 @PostMapping(value = "/upload",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)    @ResponseBody    public Result<TbFile> upload(@RequestPart(value = "file",required = true) MultipartFile file,                         @RequestParam(name = "id",required = true) Integer id,                         @RequestParam(name = "desc",required = false)  String desc,                         @RequestParam(name = "fileId",required = false) Integer fileId ){}

解释: MediaType.MULTIPART_FORM_DATA_VALUE 代表的值为multipart/form-data它会将表单的数据处理为一条消息,以标签为单元,用分隔符分开。既可以上传键值对,也可以上传文件。当上传的字段是文件时,会有Content-Type来表名文件类型;content-disposition,用来说明字段的一些信息;

二、business客户层接口@requestmapping

属性加上consumes=MediaType.MULTIPART_FORM_DATA_VALUE如下代码

   @PostMapping(value = "/upload",produces = MediaType.APPLICATION_JSON_UTF8_VALUE,            consumes = MediaType.MULTIPART_FORM_DATA_VALUE)    @ResponseBody    Result<TbFile> upload(@RequestPart(value = "file",required = true) MultipartFile file,                  @RequestParam(name = "id",required = true) Integer id,                  @RequestParam(name = "desc",required = false)  String desc,                  @RequestParam(name = "fileId",required = false) Integer fileId );

具体大概就这么多。能力有限,多多指教!!!

feign微服务间文件上传(Finchley版本)

在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_first8100feign服务提供者
feign_upload_second8101feign服务消费者

我们调用feign_upload_second的上传文件接口上传文件,feign_upload_second内部使用feign调用feign_upload_first实现文件上传。

二 、单文件上传

1 feign_upload_first服务提供者

文件上传的服务提供者接口比较简单,如下所示:

@SpringBootApplicationpublic 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 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实现文件上传的配置类

@Configurationpublic 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);}

上传文件接口

@RestControllerpublic 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);  }}

3 测试

使用postman进行测试,可以正常上传文件

三、多文件上传

既然单个文件可以上传,那么多文件应该也没问题吧,我们对上面的代码进行修改

1 feign_upload_first服务提供者

文件上传的服务提供者接口比较简单,如下所示:

@SpringBootApplicationpublic 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);  }}

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);}

上传文件接口

@RestControllerpublic 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 测试

经过测试发现,无法上传多个文件。经过检查,发现源码里底层是有对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 扩展FormEncoder支持多文件上传

扩展FormEncoder,命名为FeignSpringFormEncoder

public class FeignSpringFormEncoder extends FormEncoder {    public FeignSpringFormEncoder() {    this(new Default());  }        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);  }}

注册配置类

@Configurationpublic class FeignSupportConfig {  @Bean  public Encoder feignFormEncoder() {    return new FeignSpringFormEncoder();  }}

经过测试可以上传多个文件。

到此,关于“微服务之间怎么通过feign调用接口上传文件”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯