使用Feign扩展包实现微服务间文件上传

作者:AaronSimon 时间:2023-04-28 01:04:31 

在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扩展包实现微服务间文件上传

我们调用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();
}
}

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

来源:https://blog.csdn.net/AaronSimon/article/details/82710938

标签:Feign,文件上传
0
投稿

猜你喜欢

  • Android自定义水波纹底部导航的实现

    2022-08-23 13:12:35
  • Java 生产者/消费者问题实例详解

    2023-02-26 01:23:36
  • C# 爬虫简单教程

    2022-04-24 14:10:53
  • Android简单自定义音乐波动特效图

    2022-10-09 15:45:44
  • C# 静态变量与静态方法实例研究

    2023-04-23 04:26:00
  • Android中的LeakCanary的原理详解

    2021-06-18 09:16:40
  • c#使用资源文件的示例

    2022-05-11 00:39:39
  • Android内存泄漏的轻松解决方法

    2023-08-27 01:51:30
  • 详解spring mvc中url-pattern的写法

    2023-11-11 07:30:58
  • SpringCloud Eureka搭建的方法步骤

    2021-10-10 11:03:29
  • ionic App 解决android端在真机上tab处于顶部的问题

    2022-05-11 21:56:57
  • Android弹窗ListPopupWindow的简单应用详解

    2021-08-04 19:42:52
  • JAVA并发图解

    2023-03-26 03:11:41
  • Android性能优化之线程监控与线程统一详解

    2023-11-24 07:38:50
  • 浅谈Java基准性能测试之JMH

    2023-01-25 13:17:40
  • Java实现医院管理系统

    2023-11-22 18:36:07
  • Java设计模式之建造者模式的示例详解

    2022-02-13 18:47:34
  • 详细分析JAVA8新特性 Base64

    2022-07-09 15:41:21
  • Mybatis中的延迟加载案例解析

    2023-02-27 01:55:37
  • 使用java采集京东商城行政区划数据示例

    2023-04-17 06:31:52
  • asp之家 软件编程 m.aspxhome.com