FeignClient in Spring Cloud realizes file uploading function

  • 2021-07-24 10:51:18
  • OfStack

Project overview: Spring Cloud built micro-service, using eureka, FeignClient, now encountered FeignClient call interface does not support uploading files,

Baidu to two schemes, one is to use feign-form and feign-form-spring library to do, source code address.

The specific use method is to add maven dependency


<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>

Inject SpringFormEncoder class


@Bean
 @Primary
 @Scope("prototype")
 public Encoder multipartFormEncoder() {
 return new SpringFormEncoder();
 }

If the method parameter in FeignClient interface is a file type, use @ RequestPart annotation, and set ContentType to 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);

But encountered a problem, is not supported file array type, I looked at the source code, found that the bottom of the source code is to support MultipartFile [] type, source code has a class called SpringManyMultipartFilesWriter, is specifically for the file array type operation, but configuration to the project in the SpringFormEncoder class has no judgment of file array type, so that can not support the upload of file array.

SpringManyMultipartFilesWriter source code:


@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 source code:


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);
 }
}

From the source code of SpringFormEncoder above, we can see that SpringFormEncoder class construction added SpringManyMultipartFilesWriter instance to the processor list. However, in the encode method, only MultipartFile type is judged, but the array type is not judged, which is strange. The bottom layer has support for arrays, but the upper layer lacks corresponding judgment, and there is no test for file array type in the test package in the source code. Is it just missed in the encode method? Or is there something wrong with the support of that file array? Therefore, the judgment of encode method is not added?

So I first tried to extend the encode method to add the judgment of file array, which should support the upload of file array, so the SpringFormEncoder class source code copied out and renamed FeignSpringFormEncoder, the source code is as follows:


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);
 }
}

After testing, it can already support file array, which is a perfect solution.

Here again by the way 1 also Baidu at that time to another solution to file upload program, this program is not detailed, directly on the address of the open source code I used

I tried to solve the problem of file upload, but the problem is that FeignClient can't use the annotation of SpringMVC, but use the annotation of Feign, so I expanded the file upload function of the first method.


Related articles: