https://blog.csdn.net/qq_38288606
应用场景
第三方调用的我的接口,上传若干个文件,我用MultiPartFile[]
数组接收,之后我调用其他服务的接口,把文件发送过去,统一上传保存
存在的问题
当你使用feign
传递MultipartFile
对象时,接收方无法解析,所以,需要重写feign
的encoder
,让它支持MultipartFile
类型以及MultipartFile[]
数组类型
代码示例
我的接口如下
@ApiOperation(value = "影像上传", httpMethod = "POST")
@PostMapping(Url.IMAGE_UPLOAD)
public RestResponse imageUpload(
@RequestPart(value = "files", required = false) MultipartFile[] files,
@RequestParam(value = "type", required = false) String type) {
return noCarInsureService.imageUpload(files, type);
}
我的FeignClient
@FeignClient(
name = "tenbent-pb-insurer",
url = "http://xx.xx.xx.xx:9510",
configuration = FeignMultipartSupportConfig.class
)
public interface AeonLifeFeignClient {
@RequestMapping(value = Url.IMAGE_UPLOAD, method = RequestMethod.POST,
produces = {MediaType.APPLICATION_JSON_UTF8_VALUE},
consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
RestResponse imageUpload(
@RequestPart(value = "files", required = false) MultipartFile[] files,
@RequestParam(value = "type", required = false) String type);
}
需要注意的地方
- 添加 <font color="red">consumes = MediaType.MULTIPART_FORM_DATA_VALUE)</font>
-
MultipartFile
需要用 <font color="red">@RequestPart</font>,不能用 <font color="red">@RequestParam</font>,如果用 <font color="red">@RequestParam</font>,被调用的服务方会出现如下错误:
Caused by: org.apache.commons.fileupload.FileUploadException: the request was rejected because no multipart boundary was found
<font color="red"> 跨服务调用的方法,MultipartFile 的注解要用 @RequestPart </font>
关于@RequestPart
和@RequestParam
的区别:
当请求方法的请求参数类型不再是String
类型的时候,@RequestParam
依赖于注册Converter
或者PropertyEditor
来解析参数,而@RequestPart
则是通过HttpMessageConverter
来根据Content-Type
决定采用的消息转换器。
总结:
@RequestParam
适用于name-valueString
类型的请求域
@RequestPart
适用于复杂的请求域,例如:JSON、XML
- 设置 ContentType 为 multipart/form-data
解决办法
1. POM文件中引入以下两个jar包(让他支持任意数量文件上传)
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form</artifactId>
<version>3.2.2</version>
</dependency>
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form-spring</artifactId>
<version>3.2.2</version>
</dependency>
2. 引用配置类FeignMultipartSupportConfig,并且实例化
@Configuration
public class FeignMultipartSupportConfig {
@Autowired
private ObjectFactory<HttpMessageConverters> messageConverters;
@Bean
@Primary
@Scope("prototype")
public Encoder multipartFormEncoder() {
// 为Encoder注入messageConverters
return new SpringMultipartEncoder(new SpringEncoder(messageConverters));
}
}
3. 源码改写
feign-form-spring
包中有这样一个类:
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
类型,没有判断数组类型,因些需要改写这个类(让它支持MultipartFile
和 MultipartFile[]
),将SpringFormEncoder类源码复制出来重命名为 FeignSpringFormEncoder,改写之后的源码如下:
public class FeignMultipartSpringFormEncoder extends FormEncoder {
/**
* Constructor with the default Feign's encoder as a delegate.
*/
public FeignMultipartSpringFormEncoder() {
this(new Default());
}
/**
* Constructor with specified delegate encoder.
*
* @param delegate delegate encoder, if this encoder couldn't encode object.
*/
public FeignMultipartSpringFormEncoder(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)) {
// 单MultipartFile判断
val file = (MultipartFile) object;
val data = singletonMap(file.getName(), object);
super.encode(data, MAP_STRING_WILDCARD, template);
return;
} else if (bodyType.equals(MultipartFile[].class)) {
// MultipartFile数组处理
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);
}
}