歡迎訪問陳同學博客原文
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.0 和 io.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-form 和 OpenFeign 版本之間的關係,官方描述如下:
如何上傳多種文件
如下,假設有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。
歡迎關注公衆號 [陳一樂],一起學習,一起成長