Spring Cloud中FeignClient添加上传文件功能

项目概况:Spring Cloud搭的微服务,使用了eureka,FeignClient,现在遇到FeignClient调用接口时不支持上传文件,

百度到两种方案,一种是使用feign-form和feign-form-spring库来做,源码地址:https://github.com/OpenFeign/feign-form。

具体的使用方法是加入maven依赖
 

        <dependency>
            <groupId>io.github.openfeign.form</groupId>
            <artifactId>feign-form-spring</artifactId>
            <version>3.2.2</version>
        </dependency>
        <dependency>
            <groupId>io.github.openfeign.form</groupId>
            <artifactId>feign-form</artifactId>
            <version>3.2.2</version>
        </dependency>

注入SpringFormEncoder类

    @Bean
    @Primary
    @Scope("prototype")
    public Encoder multipartFormEncoder() {
        return new SpringFormEncoder();
    }

FeignClient接口里方法参数是文件类型的要用@RequestPart注解,且要设置ContentType为multipart/form-data

    @ResponseBody
    @RequestMapping(value = "/ctstestcase/updateTestCase", method = {RequestMethod.POST}, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    Map<String, Object> updateTestCase(@RequestParam("testcaseId") String testcaseId,
            @RequestParam("name") String name, @RequestParam("assignId") String assignId,
            @RequestParam("areaId") String areaId, @RequestParam("state") Integer state,
            @RequestParam("iterationId") String iterationId,@RequestParam("priority") Integer priority,
            @RequestParam("moduleId") String moduleId,  @RequestParam("executionType") Integer executionType,
            @RequestParam("summary") String summary, @RequestParam("tcsteps") String tcsteps,
            @RequestParam("relations") String relations,@RequestParam("attachments") String attachments,
            @RequestPart("files") MultipartFile[] files);

但遇到一个问题,就是不支持文件数组类型,我看了源码,发现源码里底层是有对MultipartFile[]类型的支持的,源码中有个类叫SpringManyMultipartFilesWriter,是专门针对文件数组类型进行操作的,但是配置到项目里的SpringFormEncoder类里却没有对文件数组类型的判断,以致不能支持文件数组的上传.。

SpringManyMultipartFilesWriter源码:
 

@FieldDefaults(level = PRIVATE, makeFinal = true)
public class SpringManyMultipartFilesWriter extends AbstractWriter {
 
  SpringSingleMultipartFileWriter fileWriter = new SpringSingleMultipartFileWriter();
 
  @Override
  public void write (Output output, String boundary, String key, Object value) throws Exception {
    if (value instanceof MultipartFile[]) {
      val files = (MultipartFile[]) value;
      for (val file : files) {
        fileWriter.write(output, boundary, key, file);
      }
    } else if (value instanceof Iterable) {
      val iterable = (Iterable<?>) value;
      for (val file : iterable) {
        fileWriter.write(output, boundary, key, file);
      }
    }
  }
 
  @Override
  public boolean isApplicable (Object value) {
    if (value == null) {
      return false;
    }
    if (value instanceof MultipartFile[]) {
      return true;
    }
    if (value instanceof Iterable) {
      val iterable = (Iterable<?>) value;
      val iterator = iterable.iterator();
      if (iterator.hasNext() && iterator.next() instanceof MultipartFile) {
        return true;
      }
    }
    return false;
  }

SpringFormEncoder源码:

public class SpringFormEncoder extends FormEncoder {
 
  /**
   * Constructor with the default Feign's encoder as a delegate.
   */
  public SpringFormEncoder () {
    this(new Encoder.Default());
  }
 
  /**
   * Constructor with specified delegate encoder.
   *
   * @param delegate  delegate encoder, if this encoder couldn't encode object.
   */
  public SpringFormEncoder (Encoder delegate) {
    super(delegate);
 
    val processor = (MultipartFormContentProcessor) getContentProcessor(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)) {
      super.encode(object, bodyType, template);
      return;
    }
 
    val file = (MultipartFile) object;
    val data = singletonMap(file.getName(), object);
    super.encode(data, MAP_STRING_WILDCARD, template);
  }
}

从上面SpringFormEncoder的源码上可以看到SpringFormEncoder类构造时把SpringManyMultipartFilesWriter实例添加到了处理器列表里了,但是在encode方法里又只判断了MultipartFile类型,没有判断数组类型,这就比较奇怪了,底层有对数组的支持但上层却缺少了相应判断,而且在源码里的test包里也没有对文件数组类型的测试,难道只是encode方法里漏掉了?还是说那个文件数组的支持有问题?所以encode方法里才没有加入对其的判断?

于是我先试着对encode方法进行扩展加入对文件数组的判断,应该就可以支持文件数组的上传了,于是把SpringFormEncoder类源码复制出来重命名为FeignSpringFormEncoder,源码如下:
 

public class FeignSpringFormEncoder extends FormEncoder {
 
    /**
     * Constructor with the default Feign's encoder as a delegate.
     */
    public FeignSpringFormEncoder() {
        this(new Encoder.Default());
    }
 
 
    /**
     * Constructor with specified delegate encoder.
     *
     * @param delegate delegate encoder, if this encoder couldn't encode object.
     */
    public FeignSpringFormEncoder(Encoder delegate) {
        super(delegate);
 
        val processor = (MultipartFormContentProcessor) getContentProcessor(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)) {
            val file = (MultipartFile) object;
            val data = singletonMap(file.getName(), object);
            super.encode(data, MAP_STRING_WILDCARD, template);
            return;
        } else if (bodyType.equals(MultipartFile[].class)) {
            val file = (MultipartFile[]) object;
            if(file != null) {
                val data = singletonMap(file.length == 0 ? "" : file[0].getName(), object);
                super.encode(data, MAP_STRING_WILDCARD, template);
                return;
            }
        }
        super.encode(object, bodyType, template);
    }
}

经过测试,已经可以支持文件数组了,完美解决。

 这里再顺便说一下当时还百度到另一个解决文件上传的方案,这个方案就不细说了,直接上我用到的那个开源代码的地址:https://github.com/pcan/feign-client-test

这个我试过也是可以解决文件上传问题的,但问题是FeignClient不能用SpringMVC的注解,得用Feign自带的注解,也因此我才扩展了第一种方法来做的文件上传功能。
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值