由于项目架构微服务SpringCloud的方式部署,独立了文件系统微服务,其他服务需要调到此服务。但是发现调用不是报错就是文件系统接收端接收到的file为空。

先抛下我的pom引用的版本支持:

1.spring-cloud-starter-openfeign 版本为 :2.1.1.RELEASE

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

2.原有依赖的 feign-form-spring 等包,用来支持跨服务间文件传输

<dependency><groupId>io.github.openfeign.form</groupId><artifactId>feign-form</artifactId><version>3.0.3</version>
</dependency>
<dependency><groupId>io.github.openfeign.form</groupId><artifactId>feign-form-spring</artifactId><version>3.0.3</version>
</dependency>

同时为了支持多服务:

3.服务端的文件上传代码如下:

/*** 上传文件 单个** @param file* @param sorId* @param fileType* @return* @throws Exception*/
@RequestMapping(value = {"/addfilesor","/pturl/addfilesor"}, produces = {MediaType.APPLICATION_JSON_UTF8_VALUE},consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public R addFileSor(@RequestPart( name ="file", value = "file", required = false) MultipartFile file,@RequestParam("sorId") String sorId,@RequestParam("fileType") String fileType) throws Exception {return sorFileService.addFileSor(file, sorId, fileType, getUser());
}
/*** 上传文件  批量** @return* @throws Exception*/
@RequestMapping(value = {"/addfilesorbath","/pturl/addfilesorbath"}   , produces = {MediaType.APPLICATION_JSON_UTF8_VALUE},consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public R addfilesorBath(@RequestPart("file") MultipartFile[] file,@RequestParam("sorId") String sorId,@RequestParam("fileType") String fileType) throws Exception {return sorFileService.addfilesorBath(file, sorId, fileType, getUser());
}

其中MultipartFile  需要用@RequestPart 来注解,@RequestPart和@RequestParam的区别请自行百度。

3.消费端代码如下:

3.1 PubAppClient 消费端接口 如下FeignMultipartSupportConfig类是为了支持跨服务文件传输做的配置。下面会讲到

@FeignClient(value = "qboa-pubapp", configuration = FeignMultipartSupportConfig.class)
public interface PubAppClient {
/*** 单个文件上传** @param file* @param sorId* @param fileType* @return* @throws Exception*/
@RequestMapping(value = "/filesor/pturl/addfilesor",produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public R addFileSor(@RequestPart(name = "file", value = "file", required = false) MultipartFile file, @RequestParam("sorId") String sorId, @RequestParam("fileType") String fileType) throws Exception;/*** 批量文件上传** @param file* @param sorId* @param fileType* @return* @throws Exception*/
@RequestMapping(value = "/filesor/pturl/addfilesorbath",produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public R addfilesorBath(@RequestPart("file") MultipartFile[] file, @RequestParam("sorId") String sorId, @RequestParam("fileType") String fileType) throws Exception;

}

3.2  FeignMultipartSupportConfig 中 最重要的一行代码:return new FeignSpringFormEncoder(new SpringEncoder(messageConverters)) 中重写,原生SpringFormEncoder 有个bug没有判断MultipartFile数组类型。

package com.qboa.oa.config.ftp;/**/***说明:*@auther Luojie*@date 2020/03/24  10:36*/import feign.codec.Encoder;
import feign.form.spring.SpringFormEncoder;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;@Configuration
public class FeignMultipartSupportConfig {@Autowiredprivate ObjectFactory<HttpMessageConverters> messageConverters;@Bean@Scope("prototype")@Primarypublic Encoder feignEncoder() {//  return new SpringFormEncoder(new SpringEncoder(messageConverters));//支持多文件传输return new FeignSpringFormEncoder(new SpringEncoder(messageConverters));}}

3.3 FeignSpringFormEncoder 类中重写 支持MultipartFile数组,网上抄的至于抄谁的我已经忘记了。。。原博主不要打死我

package com.qboa.oa.config.ftp;import feign.RequestTemplate;
import feign.codec.EncodeException;
import feign.codec.Encoder;
import feign.form.FormEncoder;
import feign.form.MultipartFormContentProcessor;
import feign.form.spring.SpringManyMultipartFilesWriter;
import feign.form.spring.SpringSingleMultipartFileWriter;
import lombok.val;
import org.springframework.web.multipart.MultipartFile;import java.lang.reflect.Type;
import java.util.Collections;
import java.util.Map;import static feign.form.ContentType.MULTIPART;/*** fegin文件解析*/
public class FeignSpringFormEncoder extends FormEncoder {/*** Constructor with the default Feign's encoder as a delegate.*/public FeignSpringFormEncoder() {this(new Encoder.Default());}/*** Constructor with specified delegate encoder.** @param delegate delegate encoder, if this encoder couldn't encode object.*/public FeignSpringFormEncoder(Encoder delegate) {super(delegate);val processor = (MultipartFormContentProcessor) getContentProcessor(MULTIPART);processor.addFirstWriter(new SpringSingleMultipartFileWriter());processor.addFirstWriter(new SpringManyMultipartFilesWriter());}@Overridepublic void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException {if (bodyType.equals(MultipartFile.class)) {MultipartFile file = (MultipartFile) object;Map<String, Object> data = Collections.singletonMap(file.getName(), object);super.encode(data, MAP_STRING_WILDCARD, template);return;} else if (bodyType.equals(MultipartFile[].class)) {MultipartFile[] file = (MultipartFile[]) object;if (file != null) {Map<String, Object> data = Collections.singletonMap(file.length == 0 ? "" : file[0].getName(), object);super.encode(data, MAP_STRING_WILDCARD, template);return;}}super.encode(object, bodyType, template);}private boolean isMultipartFileCollection(Object object) {if (!(object instanceof Iterable)) {return false;}val iterable = (Iterable<?>) object;val iterator = iterable.iterator();return iterator.hasNext() && iterator.next() instanceof MultipartFile;}
}

3.5 写接收界面传递的controller了

@RequestMapping("/saveUpload")
public R saveUpload(@RequestPart(value = "file") MultipartFile[] files,@RequestParam("patNo") String patNo,@RequestParam(value = "docId", required = false) String docId,@RequestParam("fileType") String fileType) throws Exception {Long userId = getUserId();String ret = reqAndTransMangeService.saveUpload(files, patNo, docId, fileType, userId);if (!RCodestants.SUCCESS.equals(ret)) {return R.error(ret);}return R.ok();
}

service

    @Overridepublic String saveUpload(MultipartFile[] files, String patNo, String docId, String fileType, Long userId) throws Exception {//业务代码略略略//调用文件公共方法 单个测试pubAppClient.addFileSor(files[0], patNo, fileType);//调用文件公共方法 多个测试pubAppClient.addfilesorBath(files, patNo, fileType);return RCodestants.SUCCESS;}

按照上面配置我们抛下起来出现了以下血案:

血案一:

调用的时候 the request was rejected because no multipart boundary was found

案情分析:该异常的源码抛出地方及原因可以看看

破案: 可以看到是request中boundary的为空,但是我在接受前台的时候有,跨服务的时候丢了(这些可以打印出来分析)

引发1 的Content-Type 中 multipart/form-data 可能在feign调用的时候没传递或传递的时候被更改了

相关链接https://blog.csdn.net/qq_20076823/article/details/100103358?utm_source=app

2.未使用@RequestPart (我的排除了,你们的可以检查检查)

3. 就是引入的eign-form-spring 等包 和spring-cloud-starter-openfeign 版本

相关链接https://blog.csdn.net/weixin_32885733/article/details/100993405

验证1:启动类中加了FeignClientInterceptor feign拦截器

@SpringBootApplication
@EnableFeignClients
@EnableScheduling
public class QboaOaApplication {public static void main(String[] args) {SpringApplication.run(QboaOaApplication.class, args);}@Beanpublic FeignClientInterceptor getFeignClientInterceptor() {return new FeignClientInterceptor();}
}

FeignClientInterceptor 拦截器我突发奇想,想把request都传递下去包括原有cont-type

package com.qboa.common.interceptor;import com.alibaba.fastjson.JSONObject;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;
import java.util.Collection;
import java.util.Enumeration;/*** Feign拦截器** @author Administrator* @version 1.0**/
public class FeignClientInterceptor implements RequestInterceptor {private static final Logger logger = LoggerFactory.getLogger(FeignClientInterceptor.class);@Overridepublic void apply(RequestTemplate requestTemplate) {ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();if (requestAttributes != null) {HttpServletRequest request = requestAttributes.getRequest();//取出当前请求的header,找到jwt令牌Enumeration<String> headerNames = request.getHeaderNames();if (headerNames != null) {while (headerNames.hasMoreElements()) {String headerName = headerNames.nextElement();String headerValue = request.getHeader(headerName);//临时解决支付表单改为json
//                   if("/startpay/patTransNotifyUrl" .equals(request.getServletPath())
//                           && "content-type".equals(headerName)){
//                       headerValue="application/json;charset=UTF-8";
//                   }// 将header向下传递requestTemplate.header(headerName, headerValue);}}}}
}

由此从血案1引发了血案2,导致我服务端接收的file 文件一直是null 如果接收端一直file一直是null的可以看看是否是因为这原因引起的。还有接收端的 参数名称也可以检查是否一直,检验方法就是把接收端的request和消费端传递的request中的参数打印出来对比下。

查找了下原有找到了解决方案:

在FeignClientInterceptor  的 requestTemplate.header(headerName, headerValue);之前过滤掉content-type原因可以看引发1中的链接。博主讲得简洁明了!!!

//解决跨服务文件上传 防止请求头Content-Type的boundary被更改
if ("content-type".equals(headerName)) {continue;
}

由此血案2 解决,回到血案1,时间节点一天。抱着司马当活马医的心态替换了feign form版本

<!-- Feign进行跨服务传递文件依赖 -->
<!--spring feign form 表单提交相关  请不要更换版本否则批量会报错 -->
<dependency><groupId>io.github.openfeign.form</groupId><artifactId>feign-form</artifactId><version>3.8.0</version>
</dependency><dependency><groupId>io.github.openfeign.form</groupId><artifactId>feign-form-spring</artifactId><version>3.8.0</version>
</dependency>
<!-- Feign进行跨服务传递文件依赖结束 -->

然后发现问题居然好了, 唉版本问题重于泰山,不重视的话出来的问题真的难以觉察。本着人人为我,我为人人的心,不忍心看到像我这样的菜鸟们不重复趟这趟雷,含泪写下了这篇总结,以及感谢各位无私博主,特别感谢两位大牛分享

ps:含泪哭诉,如果正好解决你的问题,请点个赞吧~~~~

论一次feign微服务直接多文件上传由版本引发的血案相关推荐

  1. input file文件上传_微服务间的文件上传与下载-Feign

    一.文件上传 (1)服务提供者 使用一个MultipartFile接收上传的文件 /** (2)Feign的Client接口 服务门面-feign的接口 pom.xml <dependencie ...

  2. 【6】使用dockerfile打包/运行微服务项目,并上传到私服harbor

    使用dockerfile打包/运行微服务项目,并上传到私服harbor 关键词 dockerfile自定义demodocker 镜像 把自定义镜像上传到私服harbor 一.环境准备 主机地址 部署 ...

  3. 谷粒学院-云存储微服务环境搭建、上传讲师图像后端接口的实现

    在service模块下创建子模块service_oss 配置pom.xml service_oss上级模块service已经引入service的公共依赖,所以service_oss模块只需引入阿里云 ...

  4. springcloud feign前后端分离实现文件上传下载

    文件上传 一.服务消费者Controller package com.biddingportal.controller;import com.alibaba.fastjson.JSON; import ...

  5. U-Mail邮件服务系统任意文件上传+执行漏洞(runtime缺陷与验证绕过)

    http://www.wooyun.org/bugs/wooyun-2010-061859 转载于:https://www.cnblogs.com/hookjoy/p/4068326.html

  6. 文件上传结合SpringCloud的Feign进行服务调用

    工作中一直使用的是SpringCloud,其中的多个组件的使用也已经有一段时间了,包括对组件的配置文件的编写,今天要讲的是SpringCloud中的一个远程服务调用的组件,使用Feign之后,我们调用 ...

  7. 前后端分离微服务管理系统项目实战SaaS-HRM项目(九)——文件上传与PDF报表入门

    文章目录 九.文件上传与PDF报表入门 1.图片上传 <1>.Data URL (1).概述 (2).入门 (3).基本原理 (4).优缺点分析 <2>.实现用户头像上传 2. ...

  8. 泛微任意文件上传(CNVD-2021-49104)

    CNVD-2021-49104 goby exp 声明 代码 poc集合 声明 本程序仅供于学习交流,请使用者遵守<中华人民共和国网络安全法>,勿将此脚本用于非授权的测试,脚本开发者不负任 ...

  9. 微服务引擎的线上流量治理最佳实践

    简介:本实践将重点介绍如何快速集成主流开源微服务框架,实现业务零改造,解决开源框架在生产落地过程中的痛点,例如无损上下线.标签路由等,并通过托管微服务开源组件(API网关.注册中心.配置中心等)的服务 ...

最新文章

  1. 大学可以学前端开发_所有开发人员在大学中应该学习的东西
  2. m行n列最大值和最小值C语言,找数组最值 按如下函数原型编程从键盘输入一个m行n列的二维数...
  3. 非常棒的 「Sublime Text 配色/主题」与「编程字体」
  4. android 8.0 三星,这些三星手机竟到2019年才能升级安卓8.0:等到头发都白了
  5. 以WinGrub 引导安装Fedora 4.0 为例,详述用WinGrub来引导Linux的安装
  6. 说说低功耗的那些事儿
  7. 【HDU - 3038】How Many Answers Are Wrong 【带权并查集 - 向量偏移】
  8. matlab2018a制图,MatLab 2018a 官方教程
  9. 2018中国企业云计算应用现状及需求调研报告
  10. java调用js模板引擎_JavaScript模板引擎用法实例
  11. Typescript基础知识--学习笔记
  12. 专注要事、把手弄脏、高效优雅是对抗规模化焦虑的好办法--读Getting Real(达成现实)和 Rework(重塑工作)
  13. 【杂烩】Tesla M40 训练机组装与散热改造
  14. grpc双向流 python_gRPC Golang/Python使用
  15. 【深度学习】使用tensorflow实现AlexNet
  16. Pacemaker-学习总结(概念、结构)
  17. [CF1153F]Serval and Bonus Problem(dp/积分+OGF)
  18. unity 游戏存档
  19. 中英保险业务常用词汇--寿险
  20. 车联网发展的昨天和明天

热门文章

  1. A*算法求解15数码问题
  2. Mysql级联操作,添加外键和删除外键
  3. python传智播客王铭东_传智播客Python学科的王铭东老师讲的怎么样 |
  4. python 关键词抽取工具
  5. [UESTC 1061]秋实大哥与战争
  6. 程序员不得不知道的 API 接口常识
  7. 生活中正确购买物品你可能真的不会!
  8. CSU 1726: 你经历过绝望吗?两次!
  9. JS检测是否有企业微信应用程序
  10. steam在matlab是什么,什么是STEAM,和STEM有何区别?