问题

由于某些原因,现在需要这样的一个文件上传接口,这个接口type(String)是必传参数,photoFile(MultipartFile)是非必传参数,即一般情况下需要接受两个参数,分别为photoFile和type,但是偶尔只接受type参数,不需要起到上传作用。

按常规写法,photoFile参数的required配置设置为了false。

奈何调试时发现,photoFile的required配置是失效的。即下面的接口写法,photoFile成了必传参数。

@ResponseBody
@RequestMapping(value = "/upload")
public String uploadPics(@RequestPart(value = "photoFile", required = false) MultipartFile photoFile,
@RequestParam(value = "type", required = true) String type, HttpServletRequest request) throws Exception {
。。。
}

当模拟upload接口请求时,如果不携带photoFile参数,如上接口写法报错如下

org.springframework.web.multipart.MultipartException: The current request is not a multipart request
at org.springframework.web.servlet.mvc.method.annotation.RequestPartMethodArgumentResolver.assertIsMultipartRequest(RequestPartMethodArgumentResolver.java:178) ~[spring-webmvc-4.0.2.RELEASE.jar:4.0.2.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestPartMethodArgumentResolver.resolveArgument(RequestPartMethodArgumentResolver.java:116) ~[spring-webmvc-4.0.2.RELEASE.jar:4.0.2.RELEASE]
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:79) ~[spring-web-4.0.2.RELEASE.jar:4.0.2.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:157) ~[spring-web-4.0.2.RELEASE.jar:4.0.2.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:124) ~[spring-web-4.0.2.RELEASE.jar:4.0.2.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104) ~[spring-webmvc-4.0.2.RELEASE.jar:4.0.2.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:749) ~[spring-webmvc-4.0.2.RELEASE.jar:4.0.2.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:690) ~[spring-webmvc-4.0.2.RELEASE.jar:4.0.2.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83) ~[spring-webmvc-4.0.2.RELEASE.jar:4.0.2.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:945) ~[spring-webmvc-4.0.2.RELEASE.jar:4.0.2.RELEASE]
...

方案

required配置失效,估计是没有走required的相关流程。

我们根据所报的第三行错误,进入RequestPartMethodArgumentResolver.class的源码的116行查看。

111		@Override
112 public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
113 NativeWebRequest request, WebDataBinderFactory binderFactory) throws Exception {
114
115 HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
116 assertIsMultipartRequest(servletRequest);
117
118 MultipartHttpServletRequest multipartRequest =
119 WebUtils.getNativeRequest(servletRequest, MultipartHttpServletRequest.class);
120
121 String partName = getPartName(parameter);
122 Object arg;
123
124 if (MultipartFile.class.equals(parameter.getParameterType())) {
125 Assert.notNull(multipartRequest, "Expected MultipartHttpServletRequest: is a MultipartResolver configured?");
126 arg = multipartRequest.getFile(partName);
}
.....

再点击116行,进入assertIsMultipartRequest方法

175		private static void assertIsMultipartRequest(HttpServletRequest request) {
176 String contentType = request.getContentType();
177 if (contentType == null || !contentType.toLowerCase().startsWith("multipart/")) {
178 throw new MultipartException("The current request is not a multipart request");
179 }
}

很明显,错误就是178行触发的。而且在上层的几个调用中也没有涉及到required的流程。

那么问题来了, 接口偶尔需要起到上传作用,如果不改变接口传参形式,就只能改源码了,很明显,这不是好的方案。

那答案肯定就在源码里了,我们很容易就想到可以在接口处完全去掉photoFile(MultipartFile)参数,然后把模仿源码,从request里去获取MultipartFile对象。

下面是根据源码118-126行、176-179行修改的接口,经测试是可行的:

@ResponseBody
@RequestMapping(value = "/upload")
public String uploadPics(@RequestParam(value = "type", required = true) String type,
HttpServletRequest request) {
....
// 检测是否为上传请求
String contentType = request.getContentType();
if (contentType != null && contentType.toLowerCase().startsWith("multipart/")) {
MultipartHttpServletRequest multipartRequest =
WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class);
MultipartFile file = multipartRequest.getFile("file");
....
}
....
}

后记

问题的处理其实很简单,但是这边文章的记录是为了另一件事。

当时实现这个功能时,发现常规写法走不通,又不好改源码,内心是有草泥马奔腾而过的。

知道答案可能在源码里,猜想可以去用request获取MultipartFile对象,但是又觉得麻烦,不想去干这事,想着可能有更好的办法,然后这事就拖着。

到后来问同事怎么解决,同事说那就从request获取MultipartFile对象咯。

根据源码用HttpServletRequest获取MultipartFile的问题的更多相关文章

  1. Flink 源码解析 —— 如何获取 ExecutionGraph ?

    https://t.zsxq.com/UnA2jIi 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0到1学习 -- Mac 上搭建 Flink 1.6. ...

  2. Flink 源码解析 —— 如何获取 JobGraph?

    JobGraph https://t.zsxq.com/naaMf6y 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0到1学习 -- Mac 上搭建 F ...

  3. Flink 源码解析 —— 如何获取 StreamGraph?

    StreamGraph https://t.zsxq.com/qRFIm6I 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0到1学习 -- Mac 上搭 ...

  4. 33、[源码]-AOP原理-获取拦截器链-MethodInterceptor

    33.[源码]-AOP原理-获取拦截器链-MethodInterceptor

  5. springMVC源码分析--AbstractHandlerMethodMapping获取url和HandlerMethod对应关系(十)

    在之前的博客springMVC源码分析--AbstractHandlerMapping(二)中我们介绍了AbstractHandlerMethodMapping的父类AbstractHandlerMa ...

  6. UiAutomator源码分析之获取控件信息

    根据上一篇文章<UiAutomator源码分析之注入事件>开始时提到的计划,这一篇文章我们要分析的是第二点: 如何获取控件信息 我们在测试脚本中初始化一个UiObject的时候通常是像以下 ...

  7. lodash源码分析之获取数据类型

    所有的悲伤,总会留下一丝欢乐的线索,所有的遗憾,总会留下一处完美的角落,我在冰峰的深海,寻找希望的缺口,却在惊醒时,瞥见绝美的阳光! --几米 本文为读 lodash 源码的第十八篇,后续文章会更新到 ...

  8. Kafka源码研究--Comsumer获取partition下标

    背景 由于项目上Flink在设置parallel多于1的情况下,job没法正确地获取watermark,所以周末来研究一下一部分,大概已经锁定了原因: 虽然我们的topic只设置了1的partitio ...

  9. springMVC源码学习之获取参数名

    1.入口到参数处理调用流程 入口为spring-webmvc-4.3.18.RELEASE.jar中org.springframework.web.servlet.DispatcherServlet. ...

随机推荐

  1. python select epoll poll的解析

    select.poll.epoll三者的区别 select select最早于1983年出现在4.2BSD中,它通过一个select()系统调用来监视多个文件描述符的数组(在linux中一切事物皆文件 ...

  2. .Net 官方学习文档

    .Net 官方学习文档:https://docs.microsoft.com/zh-cn/dotnet/articles/welcome

  3. MySQL两个最简单的delimiter的使用demo

    今天复习MySQL,使用的工具是Navicat for MySQL,写了两个简单的delimiter的demo. 第一个,获取当前时间的年月日时分秒格式的时间: delimiter $$ drop f ...

  4. Java Script 字符串操作

    JS中常用几种字符串操作: big() small() bold() fontcolor() fontsize() italics() strike() link() charAt() charCod ...

  5. 如何选择合适的PHP开发框架

    PHP作为一门成熟的WEB应用开发语言,已经深受广大开发者的青睐.与此同时,各式各样的PHP开发框架也从出不穷,面对如此多而且良莠不齐的开发框架,开发者们想必都会眼花缭乱,不知道该选择用哪个.其实并没 ...

  6. 最近公共祖先LCA(Tarjan算法)的思考和算法实现

    LCA 最近公共祖先 Tarjan(离线)算法的基本思路及其算法实现 小广告:METO CODE 安溪一中信息学在线评测系统(OJ) //由于这是第一篇博客..有点瑕疵...比如我把false写成了f ...

  7. struts2 之 Action的优化配置

    总结:struts2种action的配置文件会随着业务的增加而增加,导致配置文件膨胀.struts2中提供了三种方案来解决这个问题: 1. 动态方法调用来实现. 2. 通配符配置来解决. 3. 使用注 ...

  8. 【2017-04-18】Ado.Net C#连接数据库进行增、删、改、查

    一.简介 1.ado.net是一门数据库访问技术. 他可以通过程序来操作数据库 2.类库 Connection 类 和数据库交互,必须连接它.连接帮助指明数据库服务器.数据库名字.用户名.密码,和连接 ...

  9. 泛型(CSDN转载)

    函数的参数不同叫多态,函数的参数类型可以不确定吗? 函数的返回值只能是一个吗?函数的返回值可以不确定吗? 泛型是一种特殊的类型,它把指定类型的工作推迟到客户端代码声明并实例化类或方法的时候进行. 下面 ...

  10. ios系统判断某些适配 __IPHONE_OS_VERSION_MAX_ALLOWED

    由于app的最新设计字体是ios9之后的平方字体,但app最低支持ios7,so...想在常量配置文件类里统一适配下字体,如下: //适配字体,ios9及以上系统使用新字体--平方字体 #if __I ...