使用Feign實現(xiàn)微服務(wù)間文件傳輸
在很多時候我們會遇到微服務(wù)之間文件傳輸,很多時候我們可以通過序列化等方式解決(如圖片等)。
最近項目中有個excel上傳,以及多媒體文件上傳,直接報錯。
也試了2種解決方式,都不可行。
1.寫一個文件Encoder解析器,會出現(xiàn)其他的rest請求出現(xiàn)encoder錯誤
2.springcloud feign有一個規(guī)范,不可以傳輸2個對象,可以是一個對象帶幾個參數(shù)方式。
那么我們現(xiàn)在需要一種方式,不配置全局的解析器,而是通過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>
調(diào)用方式
@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("參數(shù)不能為空."); } 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()); } /** * 設(shè)置頭 * @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調(diào)用接口
@RequestLine("POST /punish/imExcel") ActionResult<List<String>> imPunishExcel(@Param("file") MultipartFile file, @Param("operatorId") Integer operatorId);
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
注解@TableName,@TableField,pgsql的模式對應(yīng)方式
這篇文章主要介紹了注解@TableName,@TableField,pgsql的模式對應(yīng)方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-04-04Java模擬HTTP Get Post請求 輕松實現(xiàn)校園BBS自動回帖
這篇文章主要介紹了Java模擬HTTP Get Post請求,輕松實現(xiàn)校園BBS自動回帖,感興趣的小伙伴們可以參考一下2015-12-12利用Java簡單實現(xiàn)一個代碼行數(shù)統(tǒng)計器方法實例
這篇文章主要給大家介紹了關(guān)于如何利用Java簡單實現(xiàn)一個代碼行數(shù)統(tǒng)計器的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11MAVEN_HOME、M2_HOME,maven環(huán)境變量設(shè)置方式
這篇文章主要介紹了MAVEN_HOME、M2_HOME,maven環(huán)境變量設(shè)置方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07關(guān)于mybatis一對一查詢一對多查詢遇到的問題
這篇文章主要介紹了關(guān)于mybatis一對一查詢,一對多查詢遇到的錯誤,接下來是對文章進行操作,要求查詢?nèi)课恼?,并關(guān)聯(lián)查詢作者,文章標(biāo)簽,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-05-05Mybatis-plus3.4.3下使用lambdaQuery報錯解決
最近在使用lambdaQuery().eq(CommonUser::getOpenId, openId).one()進行查詢報錯,本文主要介紹了Mybatis-plus3.4.3下使用lambdaQuery報錯解決,具有一定的參考價值,感興趣的可以了解一下2024-07-07SparkSQL讀取hive數(shù)據(jù)本地idea運行的方法詳解
這篇文章主要介紹了SparkSQL讀取hive數(shù)據(jù)本地idea運行的方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-09-09