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

使用Feign扩展包怎么实现微服务间文件上传?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。

创新互联公司始终坚持【策划先行,效果至上】的经营理念,通过多达10年累计超上千家客户的网站建设总结了一套系统有效的推广解决方案,现已广泛运用于各行各业的客户,其中包括:玻璃隔断等企业,备受客户赞扬。

单文件上传

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服务消费者

增加扩展包依赖


   io.github.openfeign.form
   feign-form
   3.3.0
  
  
   io.github.openfeign.form
   feign-form-spring
   3.3.0
  
  
   commons-fileupload
   commons-fileupload
   1.3.3

新增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 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();
 }
}

看完上述内容,你们掌握使用Feign扩展包怎么实现微服务间文件上传的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注创新互联行业资讯频道,感谢各位的阅读!


文章标题:使用Feign扩展包怎么实现微服务间文件上传
标题链接:http://scyanting.com/article/joopsc.html