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自帶的註解,也因此我才擴充套件了第一種方法來做的檔案上傳功能。