最近做项目时候遇上服务间传输MultipartFile对象的问题,网上看了一些方法,放上去基本上没有啥卵用,最后看了写源码,微微修改了下,解决了问题,记录下希望以后遇上同样问题的同学不会太费时间!
引用包
这个和网上的相同
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form</artifactId>
<version>3.8.0</version>
</dependency>
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form-spring</artifactId>
</dependency>
单文件上传
单文件上传很简单
- 配置编码器
@Configuration
public class GlobalFeignConfig {
@Autowired
private ObjectFactory<HttpMessageConverters> messageConverters;
@Bean
public Level level() {
return Level.FULL;
}
@Bean
public Encoder feignFormEncoder() {
return new SpringFormEncoder(new SpringEncoder(messageConverters));
}
}
配置到client端即可
- 注意 @RequestPart(“file”) MultipartFile file使用@RequestPart 而不是使用
多文件传输
如果直接用@RequestPart(“files”) MultipartFile[] files 会发现最后到服务提供者端总是就一个文件,明明是多个文件最后是一个!问题在哪里呢?
看一下SpringFormEncoder源码:
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import static feign.form.ContentType.MULTIPART;
import static java.util.Collections.singletonMap;
import java.lang.reflect.Type;
import java.util.HashMap;
import feign.RequestTemplate;
import feign.codec.EncodeException;
import feign.codec.Encoder;
import feign.form.FormEncoder;
import feign.form.MultipartFormContentProcessor;
import feign.form.spring.SpringManyMultipartFilesWriter;
import feign.form.spring.SpringSingleMultipartFileWriter;
import lombok.val;
import org.springframework.web.multipart.MultipartFile;
/**
* Adds support for {@link MultipartFile} type to {@link FormEncoder}.
*
* @author Tomasz Juchniewicz <tjuchniewicz@gmail.com>
* @since 14.09.2016
*/
public class SpringFormEncoder extends FormEncoder {
/**
* Constructor with the default Feign's encoder as a delegate.
*/
public SpringFormEncoder () {
this(new 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.addFirstWriter(new SpringSingleMultipartFileWriter());
processor.addFirstWriter(new SpringManyMultipartFilesWriter());
}
@Override
public void encode (Object object, Type bodyType, RequestTemplate template) throws EncodeException {
if (bodyType.equals(MultipartFile[].class)) {
val files = (MultipartFile[]) object;
val data = new HashMap<String, Object>(files.length, 1.F);
for (val file : files) {
data.put(file.getName(), file);
}
super.encode(data, MAP_STRING_WILDCARD, template);
} else if (bodyType.equals(MultipartFile.class)) {
val file = (MultipartFile) object;
val data = singletonMap(file.getName(), object);
super.encode(data, MAP_STRING_WILDCARD, template);
} else if (isMultipartFileCollection(object)) {
val iterable = (Iterable<?>) object;
val data = new HashMap<String, Object>();
for (val item : iterable) {
val file = (MultipartFile) item;
data.put(file.getName(), file);
}
super.encode(data, MAP_STRING_WILDCARD, template);
} else {
super.encode(object, bodyType, template);
}
}
private boolean isMultipartFileCollection (Object object) {
if (!(object instanceof Iterable)) {
return false;
}
val iterable = (Iterable<?>) object;
val iterator = iterable.iterator();
return iterator.hasNext() && iterator.next() instanceof MultipartFile;
}
}
关键代码如下
if (bodyType.equals(MultipartFile[].class)) {
val files = (MultipartFile[]) object;
val data = new HashMap<String, Object>(files.length, 1.F);
for (val file : files) {
data.put(file.getName(), file);
}
super.encode(data, MAP_STRING_WILDCARD, template);
}
在循环中file.getName是相同的,所以一直覆盖
问题找到了,修改很简单:
if (bodyType.equals(MultipartFile[].class)) {
val files = (MultipartFile[]) object;
val data = new HashMap<String, Object>(files.length, 1.F);
for (val file : files) {
data.put(file.getName(), files );
}
super.encode(data, MAP_STRING_WILDCARD, template);
}
file 修改为 files,测试一下,应该ok了!
代码
client 端
@RestController
@RequestMapping("customerService")
@AllArgsConstructor
@Api(tags = "客户服务")
public class CustomerController {
private final RemoteFileService remoteFileService;
/**
* 测试文件上传
*
* @return R
*/
@ApiOperation(value = "测试文件上传", notes = "测试文件上传")
@PostMapping("/test")
public Result testFile(@RequestPart("files") MultipartFile[] files) {
return Result.succeed(remoteFileService.uploadFiles(files, "userLogo/test/"));
}
}
feign service
@FeignClient(value = "file-service", configuration = {FeignExceptionConfig.class, GlobalFeignConfig.class})
public interface RemoteFileService {
/**
* 上传文件
*
* @param file
* @param fileName 指定的文件名(不指定可以为空)
* @param path
* @return
*/
@PostMapping(value = "/fileInfo/uploadFile", produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
Result<FileInfo> uploadFile(@RequestPart("file") MultipartFile file, @RequestParam("fileName") String fileName, @RequestParam("path") String path);
/**
* 上传多张图片
*
* @param files
* @param path
* @return
*/
@PostMapping(value = "/fileInfo/uploadFiles", produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
List<FileInfo> uploadFiles(@RequestPart("files") MultipartFile[] files, @RequestParam("path") String path);
}
server 端
@PostMapping("/uploadFiles")
public List<FileInfo> uploadFiles(@RequestPart("files") MultipartFile[] files, @RequestParam("path") String path) throws Exception {
……文件处理
return null;
}