Feign 上傳文件的常見問題

歡迎訪問陳同學博客原文

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 版本之間的關係,官方描述如下:

如何上傳多種文件

如下,假設有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


歡迎關注公衆號 [陳一樂],一起學習,一起成長

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章