使用Feign实现微服务间文件传输

在很多时候我们会遇到微服务之间文件传输,很多时候我们可以通过序列化等方式解决(如图片等)。

最近项目中有个excel上传,以及多媒体文件上传,直接报错。

也试了2种解决方式,都不可行。

1.写一个文件Encoder解析器,会出现其他的rest请求出现encoder错误

2.springcloud feign有一个规范,不可以传输2个对象,可以是一个对象带几个参数方式。

那么我们现在需要一种方式,不配置全局的解析器,而是通过Feign Builder 去管理上传文件,这种方式管理起来也较为方便。

引用包

<dependency>

<groupId>com.netflix.feign</groupId>

<artifactId>feign-core</artifactId>

<version>8.17.0</version>

</dependency>

<dependency>

<groupId>com.netflix.feign</groupId>

<artifactId>feign-jackson</artifactId>

<version>8.17.0</version>

</dependency>

<dependency>

<groupId>com.netflix.feign</groupId>

<artifactId>feign-slf4j</artifactId>

<version>8.17.0</version>

</dependency>

调用方式 

@ApiOperation(value = "上传Excel", notes = "上传Excel")

@RequestMapping(value = "/imExcel", method = RequestMethod.POST, produces = request_headers)

public ActionResult imExcel(@RequestBody MultipartFile file,@RequestParam("operatorId") Integer operatorId){

if(file == null || file.isEmpty()|| operatorId==null)

return new ActionResult<>(ResultType.BAD_REQUEST,"文件与操作用户ID都不能为空");

String fileName = file.getOriginalFilename();

if (!fileName.matches("^.+\\.(?i)(xls)$") && !fileName.matches("^.+\\.(?i)(xlsx)$")) {

return new ActionResult<>(ResultType.BAD_REQUEST,"上传文件格式错误,请上传后缀为.xls或.xlsx的文件");

}

Feign.Builder encoder = Feign.builder()

.decoder(new JacksonDecoder())

.encoder(new FeignEncoder());

FileUpload complainFileUpload = encoder.target(FileUpload.class,LABEL_URL);

return complainFileUpload.imComplainExcel(file,operatorId);

}

文件Encode

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.CollectionUtils;

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 lxl

*/

public class FeignEncoder implements Encoder {

private final List<HttpMessageConverter<?>> converters = new RestTemplate().getMessageConverters();

private final HttpHeaders multipartHeaders = new HttpHeaders();

private final HttpHeaders jsonHeaders = new HttpHeaders();

public static final Charset UTF_8 = Charset.forName("UTF-8");

public FeignEncoder() {

multipartHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);

jsonHeaders.setContentType(MediaType.APPLICATION_JSON);

}

@Override

public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException {

if (isFormRequest(bodyType)) {

encodeMultipartFormRequest((Map<String, ?>) object, template);

} else {

encodeRequest(object, jsonHeaders, template);

}

}

private void encodeMultipartFormRequest(Map<String, ?> formMap, RequestTemplate template) throws EncodeException {

if (CollectionUtils.isEmpty(formMap)) {

throw new EncodeException("参数不能为空.");

}

LinkedMultiValueMap<String, Object> map = new LinkedMultiValueMap<>();

for (Entry<String, ?> entry : formMap.entrySet()) {

Object value = entry.getValue();

if (isMultipartFile(value)) {

map.add(entry.getKey(), encodeMultipartFile((MultipartFile) value));

} else if (isMultipartFileArray(value)) {

encodeMultipartFiles(map, entry.getKey(), Arrays.asList((MultipartFile[]) value));

} else {

map.add(entry.getKey(), encodeJsonObject(value));

}

}

encodeRequest(map, multipartHeaders, template);

}

private boolean isMultipartFile(Object object) {

return object instanceof MultipartFile;

}

private boolean isMultipartFileArray(Object o) {

return o != null && o.getClass().isArray() && MultipartFile.class.isAssignableFrom(o.getClass().getComponentType());

}

/**

* 设置头

* @param file

* @return

*/

private HttpEntity<?> encodeMultipartFile(MultipartFile file) {

HttpHeaders filePartHeaders = new HttpHeaders();

filePartHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);

try {

Resource multipartFileResource = new MultipartFileResource(file.getOriginalFilename(), file.getSize(), file.getInputStream());

return new HttpEntity<>(multipartFileResource, filePartHeaders);

} catch (IOException ex) {

throw new EncodeException("Cannot encode request.", ex);

}

}

/**

* 映射

* @param map

* @param name

* @param files

*/

private void encodeMultipartFiles(LinkedMultiValueMap<String, Object> map, String name, List<? extends MultipartFile> files) {

HttpHeaders filePartHeaders = new HttpHeaders();

filePartHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);

try {

for (MultipartFile file : files) {

Resource multipartFileResource = new MultipartFileResource(file.getOriginalFilename(), file.getSize(), file.getInputStream());

map.add(name, new HttpEntity<>(multipartFileResource, filePartHeaders));

}

} catch (IOException ex) {

throw new EncodeException("Cannot encode request.", ex);

}

}

/**

* {@link HttpEntity} {@code Content-type}

* {@code application/json}

*

* @param o

* @return

*/

private HttpEntity<?> encodeJsonObject(Object o) {

HttpHeaders jsonPartHeaders = new HttpHeaders();

jsonPartHeaders.setContentType(MediaType.APPLICATION_JSON);

return new HttpEntity<>(o, jsonPartHeaders);

}

/**

* {@link org.springframework.web.client.RestTemplate}

*

* @param value

* @param requestHeaders

* @param template

* @throws EncodeException

*/

private void encodeRequest(Object value, HttpHeaders requestHeaders, RequestTemplate template) throws EncodeException {

ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

HttpOutputMessage dummyRequest = new HttpOutputMessageImpl(outputStream, requestHeaders);

try {

Class<?> requestType = value.getClass();

MediaType requestContentType = requestHeaders.getContentType();

for (HttpMessageConverter<?> messageConverter : converters) {

if (messageConverter.canWrite(requestType, requestContentType)) {

((HttpMessageConverter<Object>) messageConverter).write(

value, requestContentType, dummyRequest);

break;

}

}

} catch (IOException ex) {

throw new EncodeException("Cannot encode request.", ex);

}

HttpHeaders headers = dummyRequest.getHeaders();

if (headers != null) {

for (Entry<String, List<String>> entry : headers.entrySet()) {

template.header(entry.getKey(), entry.getValue());

}

}

/*

使用bytearray方式传输

*/

template.body(outputStream.toByteArray(), UTF_8);

}

/**

* {@link org.springframework.http.HttpOutputMessage}

* {@link org.springframework.http.converter.HttpMessageConverter}

*/

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;

}

@Override

public OutputStream getBody() throws IOException {

return body;

}

@Override

public HttpHeaders getHeaders() {

return headers;

}

}

static boolean isFormRequest(Type type) {

return MAP_STRING_WILDCARD.equals(type);

}

static class MultipartFileResource extends InputStreamResource {

private final String filename;

private final long size;

public MultipartFileResource(String filename, long size, InputStream inputStream) {

super(inputStream);

this.size = size;

this.filename = filename;

}

@Override

public String getFilename() {

return this.filename;

}

@Override

public InputStream getInputStream() throws IOException, IllegalStateException {

return super.getInputStream(); //To change body of generated methods, choose Tools | Templates.

}

@Override

public long contentLength() throws IOException {

return size;

}

}

}

Feign调用接口

@RequestLine("POST /punish/imExcel")

ActionResult<List<String>> imPunishExcel(@Param("file") MultipartFile file, @Param("operatorId") Integer operatorId);

以上是 使用Feign实现微服务间文件传输 的全部内容, 来源链接: utcz.com/z/348017.html

回到顶部