Spring Cloud 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.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時,就會直接報錯:
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 。
喜歡可關注公眾號[陳一樂]