使用Feign扩展包实现微服务间文件上传

在Spring Cloud 的Feign组件中并不支持文件的传输,会出现这样的错误提示:

feign.codec.EncodeException: class [Lorg.springframework.web.multipart.MultipartFile; is not a type supported by this encoder.

at feign.codec.Encoder$Default.encode(Encoder.java:90) ~[feign-core-9.5.1.jar:na]

at feign.form.FormEncoder.encode(FormEncoder.java:87) ~[feign-form-3.3.0.jar:3.3.0]

at feign.form.spring.SpringFormEncoder.encode(SpringFormEncoder.java:64) ~[feign-form-spring-3.3.0.jar:3.3.0]

但是我们可以通过使用Feign的扩展包实现这个功能。

一. 示例介绍

我们调用feign_upload_second的上传文件接口上传文件,feign_upload_second内部使用feign调用feign_upload_first实现文件上传。

二 、单文件上传

2.1 feign_upload_first服务提供者

文件上传的服务提供者接口比较简单,如下所示:

@SpringBootApplication

public class FeignUploadFirstApplication {

@RestController

public class UploadController {

@RequestMapping(value = "/uploadFile",method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)

public String handleFileUpload(@RequestPart(value = "file") MultipartFile file) {

return file.getOriginalFilename();

}

}

public static void main(String[] args) {

SpringApplication.run(FeignUploadFirstApplication.class, args);

}

}

2.2 feign_upload_second服务消费者

增加扩展包依赖

<dependency>

<groupId>io.github.openfeign.form</groupId>

<artifactId>feign-form</artifactId>

<version>3.3.0</version>

</dependency>

<dependency>

<groupId>io.github.openfeign.form</groupId>

<artifactId>feign-form-spring</artifactId>

<version>3.3.0</version>

</dependency>

<dependency>

<groupId>commons-fileupload</groupId>

<artifactId>commons-fileupload</artifactId>

<version>1.3.3</version>

</dependency>

新增feign实现文件上传的配置类

@Configuration

public class FeignSupportConfig {

@Bean

public Encoder feignFormEncoder() {

return new SpringFormEncoder();

}

}

feign远程调用接口

@FeignClient(name = "file",url = "http://localhost:8100",configuration = FeignSupportConfig.class)

public interface UploadService {

@RequestMapping(value = "/uploadFile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)

String handleFileUpload(@RequestPart(value = "file") MultipartFile file);

}

上传文件接口

@RestController

public class UploadController {

@Autowired

UploadService uploadService;

@RequestMapping(value = "/uploadFile",method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)

public String handleFileUpload(@RequestPart(value = "file") MultipartFile file) {

return uploadService.handleFileUpload(file);

}

}

2.3 测试

使用postman进行测试,可以正常上传文件

三、多文件上传

既然单个文件可以上传,那么多文件应该也没问题吧,我们对上面的代码进行修改

3.1 feign_upload_first服务提供者

文件上传的服务提供者接口比较简单,如下所示:

@SpringBootApplication

public class FeignUploadFirstApplication {

@RestController

public class UploadController {

@RequestMapping(value = "/uploadFile",method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)

public String handleFileUpload(@RequestPart(value = "file") MultipartFile file) {

return file.getOriginalFilename();

}

@RequestMapping(value = "/uploadFile2",method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)

public String handleFileUpload(@RequestPart(value = "file") MultipartFile[] file) {

String fileName = "";

for(MultipartFile f : file){

fileName += f.getOriginalFilename()+"---";

}

return fileName;

}

}

public static void main(String[] args) {

SpringApplication.run(FeignUploadFirstApplication.class, args);

}

}

3.2 feign_upload_second服务消费者

feign远程调用接口

@FeignClient(name = "file",url = "http://localhost:8100",configuration = FeignSupportConfig.class)

public interface UploadService {

@RequestMapping(value = "/uploadFile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)

String handleFileUpload(@RequestPart(value = "file") MultipartFile file);

@RequestMapping(value = "/uploadFile2", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)

String handleFileUpload(@RequestPart(value = "file") MultipartFile[] file);

}

上传文件接口

@RestController

public class UploadController {

@Autowired

UploadService uploadService;

@RequestMapping(value = "/uploadFile",method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)

public String handleFileUpload(@RequestPart(value = "file") MultipartFile file) {

return uploadService.handleFileUpload(file);

}

@RequestMapping(value = "/uploadFile2",method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)

public String handleFileUpload2(@RequestPart(value = "file") MultipartFile[] file) {

return uploadService.handleFileUpload(file);

}

}

3.3 测试

经过测试发现,无法上传多个文件。经过检查,发现源码里底层是有对MultipartFile[]类型的支持的,源码中有个类叫SpringManyMultipartFilesWriter,是专门针对文件数组类型进行操作的,但是配置到项目里的SpringFormEncoder类里却没有对文件数组类型的判断,以致不能支持文件数组的上传

SpringManyMultipartFilesWriter源码

public class SpringManyMultipartFilesWriter extends AbstractWriter {

private final SpringSingleMultipartFileWriter fileWriter = new SpringSingleMultipartFileWriter();

public SpringManyMultipartFilesWriter() {

}

public void write(Output output, String boundary, String key, Object value) throws Exception {

if (value instanceof MultipartFile[]) {

MultipartFile[] files = (MultipartFile[])((MultipartFile[])value);

MultipartFile[] var6 = files;

int var7 = files.length;

for(int var8 = 0; var8 < var7; ++var8) {

MultipartFile file = var6[var8];

this.fileWriter.write(output, boundary, key, file);

}

} else if (value instanceof Iterable) {

Iterable<?> iterable = (Iterable)value;

Iterator var11 = iterable.iterator();

while(var11.hasNext()) {

Object file = var11.next();

this.fileWriter.write(output, boundary, key, file);

}

}

}

public boolean isApplicable(Object value) {

if (value == null) {

return false;

} else if (value instanceof MultipartFile[]) {

return true;

} else {

if (value instanceof Iterable) {

Iterable<?> iterable = (Iterable)value;

Iterator<?> iterator = iterable.iterator();

if (iterator.hasNext() && iterator.next() instanceof MultipartFile) {

return true;

}

}

return false;

}

}

}

SpringFormEncoder源码

public class SpringFormEncoder extends FormEncoder {

public SpringFormEncoder() {

this(new Default());

}

public SpringFormEncoder(Encoder delegate) {

super(delegate);

MultipartFormContentProcessor processor = (MultipartFormContentProcessor)this.getContentProcessor(ContentType.MULTIPART);

processor.addWriter(new SpringSingleMultipartFileWriter());

processor.addWriter(new SpringManyMultipartFilesWriter());

}

public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException {

if (!bodyType.equals(MultipartFile.class)) {

super.encode(object, bodyType, template);

} else {

MultipartFile file = (MultipartFile)object;

Map<String, Object> data = Collections.singletonMap(file.getName(), object);

super.encode(data, MAP_STRING_WILDCARD, template);

}

}

}

从上面SpringFormEncoder的源码上可以看到SpringFormEncoder类构造时把SpringManyMultipartFilesWriter实例添加到了处理器列表里了,但是在encode方法里又只判断了MultipartFile类型,没有判断数组类型,底层有对数组的支持但上层却缺少了相应判断。那么我们可以自己去扩展FormEncoder,仿照SpringFormEncoder源码,只修改encode方法。

3.3 扩展FormEncoder支持多文件上传

扩展FormEncoder,命名为FeignSpringFormEncoder

public class FeignSpringFormEncoder extends FormEncoder {

/**

* Constructor with the default Feign's encoder as a delegate.

*/

public FeignSpringFormEncoder() {

this(new Default());

}

/**

* Constructor with specified delegate encoder.

*

* @param delegate delegate encoder, if this encoder couldn't encode object.

*/

public FeignSpringFormEncoder(Encoder delegate) {

super(delegate);

MultipartFormContentProcessor processor = (MultipartFormContentProcessor) getContentProcessor(ContentType.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)) {

MultipartFile file = (MultipartFile) object;

Map data = Collections.singletonMap(file.getName(), object);

super.encode(data, MAP_STRING_WILDCARD, template);

return;

} else if (bodyType.equals(MultipartFile[].class)) {

MultipartFile[] file = (MultipartFile[]) object;

if(file != null) {

Map data = Collections.singletonMap(file.length == 0 ? "" : file[0].getName(), object);

super.encode(data, MAP_STRING_WILDCARD, template);

return;

}

}

super.encode(object, bodyType, template);

}

}

注册配置类

@Configuration

public class FeignSupportConfig {

@Bean

public Encoder feignFormEncoder() {

return new FeignSpringFormEncoder();

}

}

经过测试可以上传多个文件。

以上是 使用Feign扩展包实现微服务间文件上传 的全部内容, 来源链接: utcz.com/z/355319.html

回到顶部