解决OpenFeign无法上传文件的问题
前言
最近在项目中使用OpenFeign时,发现其不支持文件上传功能。网上找了很多资料,最后找到feign-form和feign-form-spring的解决方案。但其默认只支持单文件上传,不支持多文件上传。解决办法为:重写Encoder类,详见三。
一、配置
1. 引入依赖
<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>
2. 在调用方增加配置
@Configuration public class AdminWebMvcConfig implements WebMvcConfigurer { @Bean public Encoder feignFormEncoder(ObjectFactory<HttpMessageConverters> messageConverters) { return new SpringFormEncoder(new SpringEncoder(messageConverters)); } }
3. 调用方启动类配置
@EnableFeignClients(basePackages = {"com.xxx.xxx"})
@SpringCloudApplication
@Import({AdminWebMvcConfig.class})
public class WebAdminApplication {
public static void main(String[] args) {
SpringApplication.run(WebAdminApplication.class, args);
}
}
二、单文件上传代码实现
- 服务调用方、接口和提供方,都使用@RequestPart注解
- 标记MediaType.MULTIPART_FORM_DATA_VALUE
1. 服务提供方Controller
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@ResponseBody
public String upload(@RequestPart(value = "file") MultipartFile file) {
......
}
2. FeignClient接口
@FeignClient(name = "xxx")
public interface FileUploadApiClient {
/**
* 文件上传
*/
@PostMapping(value = "/oss/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
String upload(@RequestPart(value = "file") MultipartFile file);
}
3. 调用方Controller
@RestController
public class FileUploadManagerController {
@Autowired
private FileUploadApiClient fileUploadApiClient;
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@ResponseBody
public String upload(@RequestPart(value = "file") MultipartFile file) {
return fileUploadApiClient.upload(file);
}
}
三、多文件上传实现
- 只需修改下面两项,即可完美支持单文件和多文件上传功能
1. 新建FeignSpringFormEncoder类
import feign.RequestTemplate;
import feign.codec.EncodeException;
import feign.codec.Encoder;
import feign.form.ContentType;
import feign.form.FormEncoder;
import feign.form.MultipartFormContentProcessor;
import feign.form.spring.SpringManyMultipartFilesWriter;
import feign.form.spring.SpringSingleMultipartFileWriter;
import org.springframework.web.multipart.MultipartFile;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.Map;
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);
}
}
2. 修改AdminWebMvcConfig
@Configuration public class AdminWebMvcConfig implements WebMvcConfigurer { @Bean public Encoder feignEncoder(ObjectFactory<HttpMessageConverters> messageConverters) { return new FeignSpringFormEncoder(new SpringEncoder(messageConverters)); } }