热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

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

这篇文章主要为大家详细介绍了使用Feign实现微服务间文件传输,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

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

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

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

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

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

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

引用包


  com.netflix.feign
  feign-core
  8.17.0


  com.netflix.feign
  feign-jackson
  8.17.0


  com.netflix.feign
  feign-slf4j
  8.17.0

调用方式 

@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("^.+\\.(&#63;i)(xls)$") && !fileName.matches("^.+\\.(&#63;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> 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) object, template);
    } else {
      encodeRequest(object, jsonHeaders, template);
    }
  }
 
 
  private void encodeMultipartFormRequest(Map formMap, RequestTemplate template) throws EncodeException {
    if (CollectionUtils.isEmpty(formMap)) {
      throw new EncodeException("参数不能为空.");
    }
    LinkedMultiValueMap map = new LinkedMultiValueMap<>();
    for (Entry 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<&#63;> 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 map, String name, List<&#63; 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<&#63;> 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<&#63;> requestType = value.getClass();
      MediaType requestCOntentType= requestHeaders.getContentType();
      for (HttpMessageConverter<&#63;> messageConverter : converters) {
        if (messageConverter.canWrite(requestType, requestContentType)) {
          ((HttpMessageConverter) 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> 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> imPunishExcel(@Param("file") MultipartFile file, @Param("operatorId") Integer operatorId);

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • 本文介绍了 Go 语言中的高性能、可扩展、轻量级 Web 框架 Echo。Echo 框架简单易用,仅需几行代码即可启动一个高性能 HTTP 服务。 ... [详细]
  • 深入解析Django CBV模型的源码运行机制
    本文详细探讨了Django CBV(Class-Based Views)模型的源码运行流程,通过具体的示例代码和详细的解释,帮助读者更好地理解和应用这一强大的功能。 ... [详细]
  • Ext JS MVC系列一:环境搭建与框架概览
    本文主要介绍了如何在项目中使用Ext JS 4作为前端框架,并详细讲解了Ext JS 4的MVC开发模式。文章将从项目目录结构、相关CSS和JS文件的引用以及MVC框架的整体认识三个方面进行总结。 ... [详细]
  • 本教程详细介绍了如何使用 Spring Boot 创建一个简单的 Hello World 应用程序。适合初学者快速上手。 ... [详细]
  • Framework7:构建跨平台移动应用的高效框架
    Framework7 是一个开源免费的框架,适用于开发混合移动应用(原生与HTML混合)或iOS&Android风格的Web应用。此外,它还可以作为原型开发工具,帮助开发者快速创建应用原型。 ... [详细]
  • 了解如何在VSCode中通过配置文件自定义调试控制台的名称。 ... [详细]
  • Python 3 Scrapy 框架执行流程详解
    本文详细介绍了如何在 Python 3 环境下安装和使用 Scrapy 框架,包括常用命令和执行流程。Scrapy 是一个强大的 Web 抓取框架,适用于数据挖掘、监控和自动化测试等多种场景。 ... [详细]
  • 本文总结了一些开发中常见的问题及其解决方案,包括特性过滤器的使用、NuGet程序集版本冲突、线程存储、溢出检查、ThreadPool的最大线程数设置、Redis使用中的问题以及Task.Result和Task.GetAwaiter().GetResult()的区别。 ... [详细]
  • 在JavaWeb开发中,文件上传是一个常见的需求。无论是通过表单还是其他方式上传文件,都必须使用POST请求。前端部分通常采用HTML表单来实现文件选择和提交功能。后端则利用Apache Commons FileUpload库来处理上传的文件,该库提供了强大的文件解析和存储能力,能够高效地处理各种文件类型。此外,为了提高系统的安全性和稳定性,还需要对上传文件的大小、格式等进行严格的校验和限制。 ... [详细]
  • MySQL Decimal 类型的最大值解析及其在数据处理中的应用艺术
    在关系型数据库中,表的设计与SQL语句的编写对性能的影响至关重要,甚至可占到90%以上。本文将重点探讨MySQL中Decimal类型的最大值及其在数据处理中的应用技巧,通过实例分析和优化建议,帮助读者深入理解并掌握这一重要知识点。 ... [详细]
  • 本文介绍了如何使用 Node.js 和 Express(4.x 及以上版本)构建高效的文件上传功能。通过引入 `multer` 中间件,可以轻松实现文件上传。首先,需要通过 `npm install multer` 安装该中间件。接着,在 Express 应用中配置 `multer`,以处理多部分表单数据。本文详细讲解了 `multer` 的基本用法和高级配置,帮助开发者快速搭建稳定可靠的文件上传服务。 ... [详细]
  • 未定义的打字稿记录:探索其成因与解决方案 ... [详细]
  • 技术分享:使用 Flask、AngularJS 和 Jinja2 构建高效前后端交互系统
    技术分享:使用 Flask、AngularJS 和 Jinja2 构建高效前后端交互系统 ... [详细]
  • 您的数据库配置是否安全?DBSAT工具助您一臂之力!
    本文探讨了Oracle提供的免费工具DBSAT,该工具能够有效协助用户检测和优化数据库配置的安全性。通过全面的分析和报告,DBSAT帮助用户识别潜在的安全漏洞,并提供针对性的改进建议,确保数据库系统的稳定性和安全性。 ... [详细]
  • 【实例简介】本文详细介绍了如何在PHP中实现微信支付的退款功能,并提供了订单创建类的完整代码及调用示例。在配置过程中,需确保正确设置相关参数,特别是证书路径应根据项目实际情况进行调整。为了保证系统的安全性,存放证书的目录需要设置为可读权限。值得注意的是,普通支付操作无需证书,但在执行退款操作时必须提供证书。此外,本文还对常见的错误处理和调试技巧进行了说明,帮助开发者快速定位和解决问题。 ... [详细]
author-avatar
美女牙医--妙菡众
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有