Feign解决服务之间传递文件、传递list,map、对象

先说下背景 , 前段时间有一个需求 , 需要将服务A生成的一个文件传递到服务B , 交予服务B去做处理 , 最开始的时候使用的spring-cloud-starter-openfeign , 发现这一块是不支持的 , 然后引入了io.github.openfeign.form ,解决 , 但过一段时间又有新需求 , 在传递文件的同时 , 还传递对象和一些其他参数 , 这个时候发现feign就有些不行了 。 这个时候引入了feign-httpclient , 暂时解决 。 用了一段时间 , 发现大文件的时候又出现了数据丢失等等问题 。 还有其他各种坑就不说了 , 都是用升级版本 , 引入其他的jar来解决的 , 但这个大文件数据丢失的问题一直不行 。
之前使用的maven重要坐标
io.github.openfeign.formfeign-form${feign-form-version}io.github.openfeign.formfeign-form-spring${feign-form-version}io.github.openfeignfeign-httpclient${feign-httpclient}决定解决这个 , 首先说下使用的版本 , 这点很重要、很重要、很重要!
使用的版本:
springboot 2.0.3.RELEASEspringcloud Finchley.RELEASE
替换为下面的maven 。 上面的那些maven地址没必要了 。
【Feign解决服务之间传递文件、传递list,map、对象】2.0.88.17.0org.springframework.cloudspring-cloud-starter-openfeigncom.netflix.feignfeign-core${netflix.feign-version}com.netflix.feignfeign-jackson${netflix.feign-version}com.netflix.feignfeign-slf4j${netflix.feign-version}org.springframeworkspring-mock${spring-mock-version}注意 , 我已经测试过 openfeign、feign-httpclient , 如果使用这些版本的并不能解决文件传递的问题 , 虽然可以接受文件 , 但是文件是残缺的 , 一定要替换成上面的maven才行 。
核心思路就是:对编码器重写 , Encoder的原理就是将每个参数json序列化 , 设置requestHeader为Multipart/form-data,采用表单请求去请求生成者提供的接口 。 这个方法能够同时发送多个实体文件 , 以及MultipartFile[]的数组.
Feign解决服务之间传递文件、传递list,map、对象文章插图
首先对编码器重写 ,
import feign.RequestTemplate;import feign.codec.EncodeException;import feign.codec.Encoder;import org.springframework.core.io.InputStreamResource;import org.springframework.core.io.Resource;import org.springframework.http.HttpEntity;import org.springframework.http.HttpHeaders;import org.springframework.http.HttpOutputMessage;import org.springframework.http.MediaType;import org.springframework.http.converter.HttpMessageConverter;import org.springframework.util.LinkedMultiValueMap;import org.springframework.web.client.RestTemplate;import org.springframework.web.multipart.MultipartFile;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.lang.reflect.Type;import java.nio.charset.Charset;import java.util.Arrays;import java.util.List;import java.util.Map;import java.util.Map.Entry;/** * @author :LX * 创建时间: 2020/10/14. 15:06 * 地点:广州 * 目的: 自定义表单编码器 。 feign 实现多pojo传输与MultipartFile上传 编码器 , 需配合开启feign自带注解使用 *用于支持多对象和文件的上传 * *Encoder的原理就是将每个参数json序列化 , 设置requestHeader为Multipart/form-data,采用表单请求去请求生成者提供的接口 。*这个方法能够同时发送多个实体文件 , 以及MultipartFile[]的数组. * *参考资料: ** 备注说明: */public class FeignSpringFormEncoder implements Encoder{private final List> converters = new RestTemplate().getMessageConverters();public static final Charset UTF_8 = Charset.forName("UTF-8");public FeignSpringFormEncoder() {}/*** 实现一个 HttpOutputMessage*/private class HttpOutputMessageImpl implements HttpOutputMessage{/*** 输出流 , 请求体*/private final OutputStream body;/*** 请求头*/private final HttpHeaders headers;public HttpOutputMessageImpl(OutputStream body, HttpHeaders headers) {this.body = body;this.headers = headers;}@Overridepublic OutputStream getBody() throws IOException {return body;}@Overridepublic HttpHeaders getHeaders() {return headers;}}/*** 判断是否表单请求* @param type* @return*/static boolean isFormRequest(Type type){return MAP_STRING_WILDCARD.equals(type);}/*** 内部静态类,保存 MultipartFile 数据*/static class MultipartFileResource extends InputStreamResource {/*** 文件名*/private final String filename;/*** 文件大小*/private final long size;/*** 构造方法* @param inputStream* @param filename* @param size*/public MultipartFileResource(InputStream inputStream, String filename, long size) {super(inputStream);this.filename = filename;this.size = size;}@Overridepublic String getFilename() {return this.filename;}@Overridepublic InputStream getInputStream() throws IOException, IllegalStateException {return super.getInputStream();}@Overridepublic long contentLength() throws IOException {return size;}}/*** 重写编码器* @param object* @param bodyType* @param template* @throws EncodeException*/@Overridepublic void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException {if (isFormRequest(bodyType)){final HttpHeaders multipartHeaders = new HttpHeaders();multipartHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);encodeMultipartFormRequest((Map