SpringCloud+Feign环境下文件上传与form-data同时存在的解决办法

最近项目转型使用SpringCloud框架下的微服务架构,各微服务之间使用Feign进行调用。期间,发现若被调用方法涉及到文件上传且仅存在单个文件时,一切正常,代码片段如下:

1     @RequestMapping(value = "/if/****/add", method = RequestMethod.POST,consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
2     JSONObject add(@RequestPart(value = "file") MultipartFile file);

但若同时需要传递其他form-data数据时,则一直报错。

1     @RequestMapping(value = "/if/****", method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
2     Object add(@RequestParam(name = "objectCode") String objectCode, @RequestPart(name = "file") MultipartFile file);

报错信息为:

 1 org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part ‘file‘ is not present
 2     at org.springframework.web.method.annotation.RequestParamMethodArgumentResolver.handleMissingValue(RequestParamMethodArgumentResolver.java:193)
 3     at org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.resolveArgument(AbstractNamedValueMethodArgumentResolver.java:109)
 4     at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)
 5     at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:158)
 6     at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:128)
 7     at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97)
 8     at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
 9     at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
10     at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
11     at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967)
12     at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)
13     at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
14     at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
15     at javax.servlet.http.HttpServlet.service(HttpServlet.java:661)
16     at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
17     at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
18     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
19     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
20     at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
21     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
22     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
23     at org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:55)
24     at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
25     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
26     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)

此时,feign日志为:

 1 2018-12-22 03:42:37.591 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] ---> POST http://assembly-uiif/if/test1/public/t1?objectCode=test%3ASat+Dec+22+03%3A42%3A31+CST+2018 HTTP/1.1
 2 2018-12-22 03:42:37.593 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] Content-Type: multipart/form-data; charset=UTF-8; boundary=167d24a8200
 3 2018-12-22 03:42:37.594 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] Content-Length: 161
 4 2018-12-22 03:42:37.595 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1]
 5 2018-12-22 03:42:37.596 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] Binary data
 6 2018-12-22 03:42:37.599 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] ---> END HTTP (161-byte body)
 7 2018-12-22 03:42:37.618  INFO 66057 --- [io-31023-exec-2] s.c.a.AnnotationConfigApplicationContext : Refreshing org.spring[email protected]65669249: startup date [Sat Dec 22 03:42:37 CST 2018]; parent: org.springframework.boot[email protected]47df5041
 8 2018-12-22 03:42:37.777  INFO 66057 --- [io-31023-exec-2] f.a.AutowiredAnnotationBeanPostProcessor : JSR-330 ‘javax.inject.Inject‘ annotation found and supported for autowiring
 9 2018-12-22 03:42:38.615  INFO 66057 --- [io-31023-exec-2] c.netflix.config.ChainedDynamicProperty  : Flipping property: assembly-uiif.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
10 2018-12-22 03:42:38.677  INFO 66057 --- [io-31023-exec-2] c.n.u.concurrent.ShutdownEnabledTimer    : Shutdown hook installed for: NFLoadBalancer-PingTimer-assembly-uiif
11 2018-12-22 03:42:38.786  INFO 66057 --- [io-31023-exec-2] c.netflix.loadbalancer.BaseLoadBalancer  : Client: assembly-uiif instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=assembly-uiif,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null
12 2018-12-22 03:42:38.796  INFO 66057 --- [io-31023-exec-2] c.n.l.DynamicServerListLoadBalancer      : Using serverListUpdater PollingServerListUpdater
13 2018-12-22 03:42:38.860  INFO 66057 --- [io-31023-exec-2] c.netflix.config.ChainedDynamicProperty  : Flipping property: assembly-uiif.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
14 2018-12-22 03:42:38.863  INFO 66057 --- [io-31023-exec-2] c.n.l.DynamicServerListLoadBalancer      : DynamicServerListLoadBalancer for client assembly-epsic-uiif initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=assembly-uiif,current list of Servers=[192.168.43.31:20008],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone;    Instance count:1;    Active connections count: 0;    Circuit breaker tripped count: 0;    Active connections per server: 0.0;]
15 },Server stats: [[Server:192.168.43.31:20008;    Zone:defaultZone;    Total Requests:0;    Successive connection failure:0;    Total blackout seconds:0;    Last connection made:Thu Jan 01 08:00:00 CST 1970;    First connection made: Thu Jan 01 08:00:00 CST 1970;    Active Connections:0;    total failure count in last (1000) msecs:0;    average resp time:0.0;    90 percentile resp time:0.0;    95 percentile resp time:0.0;    min resp time:0.0;    max resp time:0.0;    stddev resp time:0.0]
16 ]}ServerList:org.springf[email protected]114d8fc3
17 2018-12-22 03:42:39.606 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] <--- HTTP/1.1 200 (2006ms)
18 2018-12-22 03:42:39.607 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] cache-control: no-cache, no-store, max-age=0, must-revalidate
19 2018-12-22 03:42:39.608 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] content-type: application/json;charset=UTF-8
20 2018-12-22 03:42:39.608 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] date: Fri, 21 Dec 2018 19:42:39 GMT
21 2018-12-22 03:42:39.609 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] expires: 0
22 2018-12-22 03:42:39.610 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] pragma: no-cache
23 2018-12-22 03:42:39.610 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] transfer-encoding: chunked
24 2018-12-22 03:42:39.610 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] x-application-context: assembly-uiif:develop:20008
25 2018-12-22 03:42:39.610 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] x-content-type-options: nosniff
26 2018-12-22 03:42:39.613 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] x-frame-options: SAMEORIGIN
27 2018-12-22 03:42:39.613 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] x-xss-protection: 1; mode=block
28 2018-12-22 03:42:39.613 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1]
29 2018-12-22 03:42:39.615 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] {"head":{"orgId":0,"orgCode":"","vers":null,"reqId":null,"errorCode":1,"errorCodeI18n":"i18n.errorCode.1","errorMessage":"Required request part ‘file‘ is not present","errorStack":"org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part ‘file‘ is not present\n\tat org.springframework.web.method.annotation.RequestParamMethodArgumentResolver.handleMissingValue(RequestParamMethodArgumentResolver.java:193)\n\tat org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.resolveArgument(AbstractNamedValueMethodArgumentResolver.java:109)\n\tat org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)\n\tat org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:158)\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:128)\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97)\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967)\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)\n\tat org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:661)\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:742)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:55)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat com.egoonet.devtools.springstarter.iam.sso.config.web.MyFilterSecurityInterceptor.invoke(MyFilterSecurityInterceptor.java:61)\n\tat com.egoonet.devtools.springstarter.iam.sso.config.web.MyFilterSecurityInterceptor.doFilter(MyFilterSecurityInterceptor.java:31)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:110)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:317)\n\tat org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127)\n\tat org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)\n\tat com.***.web.MyFilterSecurityInterceptor.invoke(MyFilterSecurityInterceptor.java:61)\n\tat com.***.web.MyFilterSecurityInterceptor.doFilter(MyFilterSecurityInterceptor.java:31)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)\n\tat org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:114)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)\n\tat org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)\n\tat org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)\n\tat org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:150)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)\n\tat org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)\n\tat org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)\n\tat org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)\n\tat org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)\n\tat org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)\n\tat org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)\n\tat org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)\n\tat org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:214)\n\tat org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177)\n\tat org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:347)\n\tat org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:263)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:108)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:106)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:96)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478)\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803)\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459)\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\n\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)\n\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\n\tat java.lang.Thread.run(Thread.java:748)\n","userId":0,"appId":108},"info":{"now":"Sat Dec 22 03:42:39 CST 2018"}}
30 2018-12-22 03:42:39.615 DEBUG 66057 --- [io-31023-exec-2] c.e.l.c.m.t.f.TestFileFeignController    : [TestFileFeignController#t1] <--- END HTTP (12082-byte body)

很显然,feign发送的报文不对,正常的报文应该是:

 1 POST /if/test1/public/t1 HTTP/1.1
 2 Host: localhost:20008
 3 cache-control: no-cache
 4 Postman-Token: 1c879cba-fdf3-4b9a-b1ef-e3d5af7be37e
 5 Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
 6
 7 Content-Disposition: form-data; name="objectCode"
 8
 9 abcd
10
11 Content-Disposition: form-data; name="file"; filename="/Users/***/Downloads/03.jpg
12
13
14 ------WebKitFormBoundary7MA4YWxkTrZu0gW--

ok,到此可知,问题出在feign发送报文时发送错误。

查阅feign源码,参考网络资料,实现一个自定义注解:

 1 /**
 2  * Feign代理方法上,与 RequestPart 一起组成多参数模式的注解
 3  */
 4 @Target(ElementType.PARAMETER)
 5 @Retention(RetentionPolicy.RUNTIME)
 6 @Documented
 7 public @interface RequestPartParam {
 8
 9     String name() default "";
10
11     boolean required() default true;
12
13 }

实现自定义注解处理器:

 1 import feign.MethodMetadata;
 2 import org.springframework.cloud.netflix.feign.AnnotatedParameterProcessor;
 3
 4 import java.lang.annotation.Annotation;
 5 import java.lang.reflect.Method;
 6 import java.util.Map;
 7
 8 import static feign.Util.checkState;
 9 import static feign.Util.emptyToNull;
10
11 /**
12  *
13  */
14 public class RequestPartParamParameterProcessor implements AnnotatedParameterProcessor {
15
16     private static final Class<RequestPartParam> ANNOTATION = RequestPartParam.class;
17
18     @Override
19     public Class<? extends Annotation> getAnnotationType() {
20         return ANNOTATION;
21     }
22
23     @Override
24     public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
25         int parameterIndex = context.getParameterIndex();
26         Class<?> parameterType = method.getParameterTypes()[parameterIndex];
27         MethodMetadata data = context.getMethodMetadata();
28
29         if (Map.class.isAssignableFrom(parameterType)) {
30             checkState(data.queryMapIndex() == null, "Query map can only be present once.");
31             data.queryMapIndex(parameterIndex);
32
33             return true;
34         }
35
36         RequestPartParam requestPartParam = ANNOTATION.cast(annotation);
37         String name = requestPartParam.name();
38         checkState(emptyToNull(name) != null,
39                 "RequestPartParam.name() was empty on parameter %s",
40                 parameterIndex);
41         context.setParameterName(name);
42
43         data.formParams().add(name);
44
45         return true;
46     }
47
48 }

实现自定义的编码器:

 1 import feign.RequestTemplate;
 2 import feign.codec.EncodeException;
 3 import feign.codec.Encoder;
 4 import feign.form.FormEncoder;
 5 import feign.form.MultipartFormContentProcessor;
 6 import feign.form.spring.SpringManyMultipartFilesWriter;
 7 import feign.form.spring.SpringSingleMultipartFileWriter;
 8 import lombok.val;
 9 import org.springframework.web.multipart.MultipartFile;
10
11 import java.lang.reflect.ParameterizedType;
12 import java.lang.reflect.Type;
13 import java.util.HashSet;
14 import java.util.Map;
15 import java.util.Set;
16
17 import static feign.form.ContentType.MULTIPART;
18 import static java.util.Collections.singletonMap;
19
20 /**
21  *
22  */
23 public class FeignSpringFormEncoder extends FormEncoder {
24
25     public FeignSpringFormEncoder () {
26         this(new Encoder.Default());
27     }
28
29     public FeignSpringFormEncoder (Encoder delegate) {
30         super(delegate);
31
32         val processor = (MultipartFormContentProcessor) getContentProcessor(MULTIPART);
33         processor.addWriter(new SpringSingleMultipartFileWriter());
34         processor.addWriter(new SpringManyMultipartFilesWriter());
35     }
36
37     @Override
38     public void encode (Object object, Type bodyType, RequestTemplate template) throws EncodeException {
39         if ((bodyType instanceof ParameterizedType) && ((ParameterizedType) bodyType).getRawType().equals(Map.class)) {
40             val data = (Map<String, Object>) object;
41             Set<String> nullSet = new HashSet<>();
42             for (Map.Entry<String, Object> entry : data.entrySet()) {
43                 if (entry.getValue() == null) {
44                     nullSet.add(entry.getKey());
45                 }
46             }
47             for (String s : nullSet) {
48                 data.remove(s);
49             }
50             super.encode(data, MAP_STRING_WILDCARD, template);
51             return;
52         } else if (bodyType.equals(MultipartFile.class)) {
53             val file = (MultipartFile) object;
54             val data = singletonMap(file.getName(), object);
55             super.encode(data, MAP_STRING_WILDCARD, template);
56             return;
57         } else if (bodyType.equals(MultipartFile[].class)) {
58             val file = (MultipartFile[]) object;
59             if (file != null) {
60                 val data = singletonMap(file.length == 0 ? "" : file[0].getName(), object);
61                 super.encode(data, MAP_STRING_WILDCARD, template);
62                 return;
63             }
64         }
65         super.encode(object, bodyType, template);
66     }
67
68 }

定义bean:

 1     @Bean
 2     public RequestPartParamParameterProcessor requestPartParamParameterProcessor() {
 3         return new RequestPartParamParameterProcessor();
 4     }
 5
 6     @Bean
 7     public PathVariableParameterProcessor pathVariableParameterProcessor() {
 8         return new PathVariableParameterProcessor();
 9     }
10
11     @Bean
12     public RequestParamParameterProcessor requestParamParameterProcessor() {
13         return new RequestParamParameterProcessor();
14     }
15
16     @Bean
17     public RequestHeaderParameterProcessor requestHeaderParameterProcessor() {
18         return new RequestHeaderParameterProcessor();
19     }

注意,参阅  org.springframework.cloud.netflix.feign.support.SpringMvcContract 中的代码可知,使用自定义注解处理器时,必须自行处理另外3个系统默认注解处理器。

至此,重新编译,运行,工作正常。问题解决。

原文地址:https://www.cnblogs.com/oilamp/p/10159741.html

时间: 2024-09-30 05:43:13

SpringCloud+Feign环境下文件上传与form-data同时存在的解决办法的相关文章

SpringCloud+Feign环境下文件上传与form-data同时存在的解决办法(2)

书接上文. 上文中描述了如何在 SpringCloud+Feign环境下上传文件与form-data同时存在的解决办法,实践证明基本可行,但却会引入其他问题. 主要导致的后果是: 1. 无法与普通Feign方法并存 2. 几率性(不确定条件下)导致其他form-data类型参数无法识别,无法正常工作,错误信息大致如下: org.springframework.web.multipart.support.MissingServletRequestPartException: Required re

上一篇写的文件上传,这一篇写文件上传中回报的错误,以及解决办法

文件上传不了,有以下几种情况: 1).form表单后面的enctype="multipart/form-data"没有,导致无法以流的形式写入. 2).第一条有的话,当点击提交的时候,直接返回strut中的error返回结果,没有fileerror提示或者提示文件太大. 原因:是因为你上传的文件或者图片大小位于struts中拦截器配置中文件大小限制的值(总)(<constant name="struts.multipart.maxSize" value=&qu

Spring MVC环境中文件上传大小和文件类型限制以及超大文件上传bug问题

    在上一篇文章中,主要介绍了Spirng MVC环境下的正常情况下文件上传功能实现.在实际开发的时候,还会涉及到上传文件大小和类型的限制,接下来就会对Spirng MVC环境下文件上传大小和类型的限制进行介绍,还会讲解到文件上传大小tomcat服务器bug问题及解决方案. 一.文件上传大小限制 这里还是接着上篇文章先介绍Spring MVC下的文件上传大小限制,文件上传大小的限制在springmvc-config.xml中配置文件解析器CommonsMultipartResolver时即可

windows 下文件上传到fastdfs

php.ini 配置 [fastdfs]; the base pathfastdfs_client.base_path = D:/tmp; connect timeout in seconds; default value is 30sfastdfs_client.connect_timeout = 2; network timeout in seconds; default value is 30sfastdfs_client.network_timeout = 60 ; standard l

asp.net 文件上传出错:Maximum request length exceeded 解决方法

<configuration>    <system.web>               <httpRuntime maxRequestLength="102400" useFullyQualifiedRedirectUrl="true" minFreeThreads="8" minLocalRequestFreeThreads="4" appRequestQueueLimit="10

Web下文件上传下载的路径问题

工程结构 1.生成一个文件到指定文件夹下 //产生一个唯一的名字 this.setFileName(String.valueOf(System.currentTimeMillis())); String path = ServletActionContext.getServletContext().getRealPath("/template/WordExportTemplate"); //工程下的完整路径名 String filepath = path +"\\"

asp.net mvc下文件上传

典型的文件上传表单 <form action="/File" enctype="multipart/form-data" method="post"/> <input type="file" name="file" /> <input type="submit" name="upload" value="提交" /&g

ssm框架下文件上传

springmvc实现文件上传的步骤: 1.页面上,通过input来准备file组件,该标签,必须给定name属性值 同时,要求form表单必须给定一个属性:enctype="multipart/form-data" 2.在pom.xml文件中,添加文件上传的第三方工具: commons-fileupload-1.3.2.jar commons-io-2.2.jar 3.在app-springmvc.xml配置文件中,准备上传操作的对象:CommonsMultipartResolver

文件上传之form表单篇

form表单上传文件 作为本系列的最后一篇,也是楼主知道的第三种文件上传的方式--隆重推出Form表单 这是最传统的上传文件,提交数据的方式 Html: 1 <form action="/home/receive" method="post" enctype="multipart/form-data"> 2 <input type="file" name="f1" id="f1&