微服务中通常使用 Feign 作为服务消费者,那么如何使用 Feign 接口传输文件呢?

一、配置文件解析器

服务提供者和消费者都需要配置文件解析器,这里使用commons-fileupload替换原有的解析器:

依赖:

1<dependency>
2    <groupId>commons-fileupload</groupId>
3    <artifactId>commons-fileupload</artifactId>
4    <version>1.3.1</version>
5</dependency>

注入bean

1@Bean(name = "multipartResolver")
2public MultipartResolver mutipartResolver() {
3    CommonsMultipartResolver com = new CommonsMultipartResolver();
4    com.setDefaultEncoding("utf-8");
5    return com;
6}

程序入口中剔除原有的解析器:

1@SpringBootApplication(exclude = {MultipartAutoConfiguration.class})

二、服务提供者,即接收文件一方的配置

Controller的写法:

 1@ResponseBody
 2@RequestMapping(value = "/upload", method = {RequestMethod.POST},
 3                produces = {MediaType.APPLICATION_JSON_UTF8_VALUE},
 4                consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
 5public Result<String> uploadFile(@RequestPart("file") MultipartFile file,
 6                                  @RequestParam("id") Long id) {
 7    String fileName = file.getOriginalFilename();
 8    String extend = FileOperateUtil.suffix(fileName);
 9    FileOperateUtil.copy("E:\\" + fileName, file);
10    return ResultBuilder.success("ok");
11}

@RequestPart指定文件,后面的@RequestParam是额外参数,注意额外参数不能超过url长度限制。

三、服务消费者配置

依赖:

 1<dependency>
 2    <groupId>io.github.openfeign.form</groupId>
 3    <artifactId>feign-form-spring</artifactId>
 4    <version>3.2.2</version>
 5</dependency>
 6<dependency>
 7    <groupId>io.github.openfeign.form</groupId>
 8    <artifactId>feign-form</artifactId>
 9    <version>3.2.2</version>
10</dependency>

文件编码配置:

 1import feign.codec.Encoder;
 2import feign.form.spring.SpringFormEncoder;
 3import org.springframework.beans.factory.ObjectFactory;
 4import org.springframework.beans.factory.annotation.Autowired;
 5import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
 6import org.springframework.cloud.netflix.feign.support.SpringEncoder;
 7import org.springframework.context.annotation.Bean;
 8import org.springframework.context.annotation.Configuration;
 9
10@Configuration
11public class MultipartSupportConfig {
12
13    @Autowired
14    private ObjectFactory<HttpMessageConverters> messageConverters;
15
16    @Bean
17    public Encoder feignFormEncoder() {
18        return new SpringFormEncoder(new SpringEncoder(messageConverters));
19    }
20
21}

Feign接口定义:

 1@FeignClient(name = "test-upload")
 2public interface UploadService {
 3
 4    @ResponseBody
 5    @RequestMapping(value = "/upload", method = {RequestMethod.POST},
 6            produces = {MediaType.APPLICATION_JSON_UTF8_VALUE},
 7            consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
 8    Result<String> uploadFile(@RequestPart("file") MultipartFile file,
 9                               @RequestParam("id") Long id);
10
11}

与普通Feign接口写法差不多,注意方法注解和参数与服务提供者的controller一样。

Controller的写法,Controller中接收前端传过来的文件信息和额外参数,然后通过Feign接口传输到远端:

 1
 2// 注入 feign 接口
 3@Autowired
 4private UploadService uploadService;
 5
 6@RequestMapping(value = "/upload", method = RequestMethod.POST, produces = "application/json; charset=utf-8")
 7@ResponseBody
 8public Result<String> testUpload(HttpServletRequest request, Long id) {
 9    Result<String> result = null;
10    MultipartHttpServletRequest mRequest = (MultipartHttpServletRequest) request;
11    Map<String, MultipartFile> fileMap = mRequest.getFileMap();
12    for (MultipartFile mFile : fileMap.values()) {
13        String fileName = mFile.getOriginalFilename();
14        result = uploadService.uploadFile(mFile, id);
15    }
16    return result;
17}

四、总结

最后梳理一下流程,服务消费者接收前端(如浏览器)传过来的文件,但是并不进行业务处理,然后通过Feign调用接口,把文件传给服务提供者,服务提供者拿到文件后,进行相应的业务处理。