使用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();
 }
}

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • SpringCloud使用Feign文件上传、下载

    文件上传.下载也是实际项目中会遇到的场景,本篇我们介绍下springcloud中如何使用feign进行文件上传与下载. 还是使用feign 进行http的调用. 一.Feign文件上传 服务提供方java代码: /** * 文件上传 * @param file 文件 * @param fileType * @return */ @RequestMapping(method = RequestMethod.POST, value = "/uploadFile", produces = {

  • Spring Cloud中FeignClient实现文件上传功能

    项目概况:Spring Cloud搭的微服务,使用了eureka,FeignClient,现在遇到FeignClient调用接口时不支持上传文件, 百度到两种方案,一种是使用feign-form和feign-form-spring库来做,源码地址. 具体的使用方法是加入maven依赖 <dependency> <groupId>io.github.openfeign.form</groupId> <artifactId>feign-form-spring&l

  • Feign实现跨服务文件上传下载

    本文实例为大家分享了Feign实现跨服务的文件上传下载操作,供大家参考,具体内容如下 1.跨服务文件上传,目前feign不支持调用文件上传接口,需要自行配置来满足feign的调用方式 ①.首先需要在pom文件里添加feign依赖 <dependency> <groupId>io.github.openfeign.form</groupId> <artifactId>feign-form-spring</artifactId> <versio

  • vue使用el-upload上传文件及Feign服务间传递文件的方法

    一.前端代码 <el-upload class="step_content" drag action="string" ref="upload" :multiple="false" :http-request="createAppVersion" :data="appVersion" :auto-upload="false" :limit="1&quo

  • 使用Spring Cloud Feign上传文件的示例

    最近经常有人问Spring Cloud Feign如何上传文件.有团队的新成员,也有其他公司的兄弟.本文简单做个总结-- 早期的Spring Cloud中,Feign本身是没有上传文件的能力的(1年之前),要想实现这一点,需要自己去编写Encoder 去实现上传.现在我们幸福了很多.因为Feign官方提供了子项目feign-form ,其中实现了上传所需的 Encoder . 注:笔者测试的版本是Edgware.RELEASE.Camden.Dalston同样适应本文所述. 加依赖 <depen

  • Spring Cloud Feign的文件上传实现的示例代码

    在Spring Cloud封装的Feign中并不直接支持传文件,但可以通过引入Feign的扩展包来实现,本来就来具体说说如何实现. 服务提供方(接收文件) 服务提供方的实现比较简单,就按Spring MVC的正常实现方式即可,比如: @RestController public class UploadController { @PostMapping(value = "/uploadFile", consumes = MediaType.MULTIPART_FORM_DATA_VAL

  • 使用Feign实现微服务间文件下载

    在使用Feign做服务间调用的时候,当下载大的文件会出现堆栈溢出的情况.另外,文件管理服务(服务提供者)文件下载接口无返回值,是通过HttpServletRespoonse传输的流数据来响应,那么服务消费者该如何接受下载的数据呢? 一. 示例介绍 我们调用feign_upload_second的下载文件接口下载文件,feign_upload_second内部使用feign调用feign_upload_first实现文件下载. 二.feign_upload_first服务提供者 服务提供者下载文件

  • 使用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:

  • node+axios实现服务端文件上传示例

    目录 一.接口文档介绍 二.文件上传 2-1.初始化项目 2-2.安装插件依赖 2-3.上传核心代码 2-4.执行脚本 最近调研企业wx集成H5应用,其中有一个发送图文消息,需要上传图片到临时素材库.之前做过文件上传到阿里云.七牛云都是服务端提供封装好的文件上传接口,在客户端使用ajax的方式上传.所以就来踩踩坑,使用node+axios实现服务端文件上传. 一.接口文档介绍 请求方式:POST(HTTPS) 请求地址:https://qyapi.weixin.qq.com/cgi-bin/me

  • 使用Feign实现微服务间文件传输

    在很多时候我们会遇到微服务之间文件传输,很多时候我们可以通过序列化等方式解决(如图片等). 最近项目中有个excel上传,以及多媒体文件上传,直接报错. 也试了2种解决方式,都不可行. 1.写一个文件Encoder解析器,会出现其他的rest请求出现encoder错误 2.springcloud feign有一个规范,不可以传输2个对象,可以是一个对象带几个参数方式. 那么我们现在需要一种方式,不配置全局的解析器,而是通过Feign Builder 去管理上传文件,这种方式管理起来也较为方便.

  • Android Volley扩展实现支持进度条的文件上传功能

    volley是一个轻量级的开源网络通信框架,开源的好处就是可以自由定制自己需要的jar包.volley里网络通信时android2.3以上用的HttpUrlConnection,2.3以下用的HttpClient,我做的改动只考虑了2.3以上,不支持2.3版本以下.HttpUrlConnection默认传输数据是将数据全部写到内存中再发送到服务端,Volley就是采用默认的方式,这样在上传大文件时很容易就out of memory,有一种解决办法是设置每次传输流的大小: 已知文件大小:conne

  • SpringBoot整合阿里云OSS对象存储服务实现文件上传

    1. 准备工作: 一.首先登录阿里云OSS对象存储控制台创建一个Bucket作为你的存储空间. 二.创建Access Keyan按要求创建进行,这里的方法步骤我就不展现出来了,你们可以自行查询阿里云文档,这个获取值本身就不难. 重点:记下你的AccessKey ID.AccessKey Secret以及你刚才创建的Buacket名字BucketName. 2. 配置: 在pom里引入oss要用的依赖 <dependency> <groupId>com.aliyun.oss</

  • Spring Cloud Feign实现文件上传下载的示例代码

    目录 独立使用Feign 上传文件 下载文件 使用Spring Cloud Feign 上传文件 下载文件 总结 Feign框架对于文件上传消息体格式并没有做原生支持,需要集成模块feign-form来实现. 独立使用Feign 添加模块依赖: <!-- Feign框架核心 --> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-core</arti

  • 微服务间调用Retrofit在Spring Cloud Alibaba中的使用

    目录 前置知识 搭建 使用 集成与配置 服务间调用 服务限流 熔断降级 总结 前置知识 在微服务项目中,如果我们想实现服务间调用,一般会选择Feign.之前介绍过一款HTTP客户端工具Retrofit,配合SpringBoot非常好用!其实Retrofit不仅支持普通的HTTP调用,还能支持微服务间的调用,负载均衡和熔断限流都能实现.今天我们来介绍下Retrofit在Spring Cloud Alibaba下的使用,希望对大家有所帮助! SpringBoot实战电商项目mall(50k+star

  • 解决nginx+lua搭建文件上传下载服务问题

    导语 项目需要做一个文件上传下载服务,利用 nginx+lua 做一个代理服务,上传入口统一,分发到不同的机器存储,下载链接和物理存储隔离,支持添加 agent 的方式扩容,这里主要讲一下思路和搭建配置过程,大神勿喷. 主要逻辑 上传 前端请求 nginx 服务, nginx 调用 upload 脚本,脚本通过查找配置,找到对应的逻辑存储路径和物理存储机器的 agent 的 ip 和端口,通过 tcp 发包到对应 agent ,部署在对应机器的 agent 接受数据,并写到本地文件. 下载 ht

随机推荐