Feign 上传文件的常见问题

作者: 码代码的陈同学 | 来源:发表于2019-04-10 23:15 被阅读310次

    欢迎访问陈同学博客原文

    Feign 作为 Spring Cloud 中 RPC 工具,利用注解来描述接口,简化了 Java HTTP Client 的调用过程,隐藏了实现细节。

    本文将介绍利用 Feign 上传文件的几个常见问题。

    • 如何上传一个/组文件
    • 如何上传多种文件
    • MultipartFile 参数不能为空问题
    • 未提供 MultipartFile 参数接口报 no multipart boundary was found 问题

    如何上传一个/组文件

    OpenFeign 默认不支持文件参数,但提供了 feign-form 拓展工具,这里简单拓展下官方Demo。

    引入 io.github.openfeign.form:feign-form:3.8.0io.github.openfeign.form:feign-form-spring:3.8.0 maven依赖,注入 SpringFormEncoder ,在 @FeignClient 中配下configuration即可。

    @FeignClient(value = "cms-service", configuration = CmsService.MyConfig.class)
    public interface CmsService {
        // 也可以使用MultipartFile[]上传多个文件
        @PostMapping(value = "upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
        void upload(@RequestPart("file") MultipartFile file);
    
        class MyConfig {
            @Bean
            public Encoder feignFormEncoder() {
                return new SpringFormEncoder();
            }
        }
    }
    

    如果需要使用Spring标准的encoder,config变一下。

    class MyConfig {
      @Autowired
      private ObjectFactory<HttpMessageConverters> messageConverters;
    
      @Bean
      public Encoder feignFormEncoder () {
        return new SpringFormEncoder(new SpringEncoder(messageConverters));
      }
    }
    

    需要特别注意 feign-formOpenFeign 版本之间的关系,官方描述如下:

    image

    如何上传多种文件

    如下,假设有file1、file2两个文件,且不是数组。

    @FeignClient(value = "cms-service")
    public interface CmsService {
        @PostMapping(value = "upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
        void upload(@RequestPart("file1") MultipartFile file1,
                   (@RequestPart("file2") MultipartFile file2);
    }
    

    在应用启动时处理CmsService时,就会直接报错:

    java.lang.IllegalStateException: Method has too many Body parameters
    

    Feign 不支持多个body参数。本身一次上传多个文件场景少见,改为每次传一个就好。

    MultipartFile 参数不能为空

    假设有MultipartFile类型参数,但 required 设置为 false

    @FeignClient(value = "cms-service")
    public interface CmsService {
        @PostMapping(value = "upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
        void upload(@RequestPart(value = "file", required = false) MultipartFile file);
    }
    

    会报错:

    Caused by: java.lang.IllegalArgumentException: Body parameter 6 was null
    

    这个问题 Body parameter was null problem when MultipartFile param is null 最近我在Github问过,解释是 Feign不支持这种特性,如果有需要,可以通过设置多个API解决,例子如下:

    public interface MailClient {
       @PostMapping("/send", consumes = MediaType.APPLICATION_FORM_URL_ENCODED)
       void send(@RequestParam("message") String message);
    
       @PostMapping("/send", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
       void send(@RequestParam("message") String message, 
                 @RequestPart("attachment") MultipartFile file);
    }
    

    no multipart boundary was found 问题

    上次一步的例子会引发新的问题,假设一个接口提供了两个参数。

    @RestController
    public class FileController {
       @PostMapping("/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
       void upload(@RequestParam("message") String message, 
                   @RequestPart("attachment") MultipartFile file);
    }
    

    但使用时未提供MultipartFile类型参数。

    @FeignClient(value = "cms-service")
    public interface CmsService {
        @PostMapping(value = "upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
        void upload(@RequestParam("message") String message, );
    }
    

    将会报如下错误:

    org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found
    

    这是因为Feign只有存在MultipartFile类型参数时才会设置 boundary。在Feign的 MultipartFormContentProcessor 中,其中有一点就专门是用来添加boundary。

    public void process(...) throws EncodeException {
        String boundary = Long.toHexString(System.currentTimeMillis());
      ...
        output.write("--").write(boundary).write("--").write("\r\n");
        String contentTypeHeaderValue = this.getSupportedContentType().getHeader() + "; charset=" + charset.name() + "; boundary=" + boundary;
        template.header("Content-Type", new String[]{contentTypeHeaderValue});
    }
    

    如果自己处理的话,可以在 RequestInterceptor 的实现类中模拟上面的方法,为 multipart/form-data 格式自定义一个boundary。

    小结

    本文是一遍工具使用帖,小结一下,传文件注意几个点:

    • Feign 不支持多个body参数,body参数也不能为空
    • 特别注意 feign-form 的版本

    若对Feign源码感兴趣,可看看 Spring Cloud 源码学习之 Feign


    欢迎关注公众号 [陈一乐],一起学习,一起成长


    image

    相关文章

      网友评论

        本文标题:Feign 上传文件的常见问题

        本文链接:https://www.haomeiwen.com/subject/jiiaiqtx.html