How to upload files between microservices through feign call interface
- 2021-10-15 10:29:21
- OfStack
Specific needs:
Our project is based on springcloud microservice of springboot framework. The technical level of back-end services is divided into business services and core services as a whole, The business service is used as an application layer, Direct connection to the client, usually used to aggregate data, core service is used to control the database according to different requirements of the specific operation of the client, file upload is through the client upload interface, through business service, and the server calls feign interface, which is also the first time to do this kind of file transfer, and encounter various problems. The following is my own solution, don't spray it if you don't like it, and the code is small white;
1. core service layer interface @ requestmapping
Attribute plus consumes=MediaType.MULTIPART_FORM_DATA_VALUE
@PostMapping(value = "/upload",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@ResponseBody
public Result<TbFile> upload(@RequestPart(value = "file",required = true) MultipartFile file,
@RequestParam(name = "id",required = true) Integer id,
@RequestParam(name = "desc",required = false) String desc,
@RequestParam(name = "fileId",required = false) Integer fileId )
Explanation: @ RequestMapping has the following two properties:
1.String[] consumes() default {};
2.String[] produces() default {};
Explanation and reference examples of two attributes:
① Attribute produces: Specifies the return value type, and can set the return value type and the character code of the return value; Refer to the following code examples:
json data is returned when the attribute produces= "application/json"
Attribute produces= "MediaType.APPLICATION_JSON_VALUE; When charset=utf-8 ", set the character code of the returned data to utf-8
@Controller
@RequestMapping(value = "/getperson", method = RequestMethod.GET, produces="application/json")
public Object getPerson(int id) {
// Implement your own logical call
Person p= new person();
return p;
}
Special note: produces= "application/json" and annotation @ ResponseBody have the same effect. If annotation is used, it is unnecessary to use this attribute
② Attribute consumes: Specify the submission content types in the processing request (Content-Type): application/json, text/html, etc.;
Refer to the following code examples:
@PostMapping(value = "/upload",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@ResponseBody
public Result<TbFile> upload(@RequestPart(value = "file",required = true) MultipartFile file,
@RequestParam(name = "id",required = true) Integer id,
@RequestParam(name = "desc",required = false) String desc,
@RequestParam(name = "fileId",required = false) Integer fileId ){
}
Explanation: MediaType. MULTIPART_FORM_DATA_VALUE represents a value of multipart/form-data. It treats the form's data as a message, separated by a separator in units of labels. You can upload both key-value pairs and files. When the uploaded field is a file, there will be Content-Type to list the file type; content-disposition, which is used to explain 1 information of the field;
2. business Client Layer Interface @ requestmapping
Attribute plus the following code for consumes=MediaType.MULTIPART_FORM_DATA_VALUE
@PostMapping(value = "/upload",produces = MediaType.APPLICATION_JSON_UTF8_VALUE,
consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@ResponseBody
Result<TbFile> upload(@RequestPart(value = "file",required = true) MultipartFile file,
@RequestParam(name = "id",required = true) Integer id,
@RequestParam(name = "desc",required = false) String desc,
@RequestParam(name = "fileId",required = false) Integer fileId );
That's about it. Limited ability, give me more advice! ! !
feign inter-microservice file upload (Finchley version)
File transfer is not supported in the Feign component of Spring Cloud and an error message appears:
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]
However, we can achieve this function by using the extension pack of Feign.
1. Example introduction
服务名 | 端口号 | 角色 |
---|---|---|
feign_upload_first | 8100 | feign服务提供者 |
feign_upload_second | 8101 | feign服务消费者 |
We call the upload file interface of feign_upload_second to upload files, and feign_upload_second uses feign to call feign_upload_first to upload files.
2. Single file upload
2.1 feign_upload_first Service Provider
The service provider interface for file upload is relatively simple, as follows:
@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 Service Consumer
Adding extension pack dependencies
<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>
New configuration class for feign to upload files
@Configuration
public class FeignSupportConfig {
@Bean
public Encoder feignFormEncoder() {
return new SpringFormEncoder();
}
}
feign Remote Call Interface
@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);
}
Upload file interface
@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 Test
Using postman for testing, files can be uploaded normally
3. Upload multiple files
Since a single file can be uploaded, so many files should be no problem, let's modify the above code
3.1 feign_upload_first Service Provider
The service provider interface for file upload is relatively simple, as follows:
@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 Service Consumers
feign Remote Call Interface
@Controller
@RequestMapping(value = "/getperson", method = RequestMethod.GET, produces="application/json")
public Object getPerson(int id) {
// Implement your own logical call
Person p= new person();
return p;
}
0
Upload file interface
@Controller
@RequestMapping(value = "/getperson", method = RequestMethod.GET, produces="application/json")
public Object getPerson(int id) {
// Implement your own logical call
Person p= new person();
return p;
}
1
3.3 Testing
After testing, it was found that multiple files could not be uploaded. After checking, found that the bottom layer of the source code is to support MultipartFile [] type, source code has a class called SpringManyMultipartFilesWriter, is specifically for the file array type operation, but configuration to the project in the SpringFormEncoder class does not have the judgment of the file array type, so that can not support the upload of the file array
SpringManyMultipartFilesWriter source code
@Controller
@RequestMapping(value = "/getperson", method = RequestMethod.GET, produces="application/json")
public Object getPerson(int id) {
// Implement your own logical call
Person p= new person();
return p;
}
2
SpringFormEncoder source code
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);
}
}
}
From the source code of SpringFormEncoder above, we can see that SpringFormEncoder class construction adds SpringManyMultipartFilesWriter instance to the processor list, but only judges MultipartFile type in encode method, and does not judge array type. The bottom layer has support for array but the upper layer lacks corresponding judgment. Then we can extend the FormEncoder by ourselves, imitate the source code of SpringFormEncoder, and only modify the encode method.
3.3 Extend FormEncoder to support multiple file uploads
Extended FormEncoder, named FeignSpringFormEncoder
@Controller
@RequestMapping(value = "/getperson", method = RequestMethod.GET, produces="application/json")
public Object getPerson(int id) {
// Implement your own logical call
Person p= new person();
return p;
}
4
Register configuration classes
@Controller
@RequestMapping(value = "/getperson", method = RequestMethod.GET, produces="application/json")
public Object getPerson(int id) {
// Implement your own logical call
Person p= new person();
return p;
}
5
After testing, you can upload multiple files.