问题:使用Spring MVC上传大文件,发现从页面提交,到进入后台controller,时间很长。怀疑是文件上传完成后,才进入。由于在HTTP首部自定义了“Token”字段用于权限校验,Token的有效时间很短,因此上传大文件时,就会验证Token失败。

示例代码:

前端:

<form action="upload" enctype="multipart/form-data" method="post">
<table>
<tr>
<td>文件描述:</td>
<td><input type="text" name="description"></td>
</tr>
<tr>
<td>请选择文件:</td>
<td><input type="file" name="file"></td>
</tr>
<tr>
<td><input type="submit" value="上传"></td>
</tr>
</table>
</form>

controller:

@RequestMapping(value="/upload",method=RequestMethod.POST)
public String upload(HttpServletRequest request,
@RequestParam("description") String description,
@RequestParam("file") MultipartFile file) throws Exception {
System.out.println("enter controller."); // 文件上传完才打印
}

springmvc-config.xml配置:

  <bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize">
<value>524288000</value>
</property>
<property name="defaultEncoding">
<value>UTF-8</value>
</property>
</bean>

Spring MVC上传文件使用了Commons FileUpload类库,即CommonsMultipartResolver使用commons Fileupload来处理 multipart请求,将Commons FileUpload的对象转换成了Spring MVC的对象。

那如果直接使用Commons FileUpload来进行文件上传下载呢?

示例代码:

protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
System.out.println("enter servlet"); // 页面提交后,立马打印
              // 省略获取上传文件的逻辑
              // ...
}

发现从页面提交,很快就进入了servlet。

既然Spring也是使用了Commons FileUpload类库,但为什么差别这么大呢?Spring在转换过程中做了什么其他操作呢?

查看源码,或者直接看([Java] SpringMVC工作原理之四:MultipartResolver

发现有个resolveLazily参数是判断是否要延迟解析文件(通过XML可以设置)。当 resolveLazily为false(默认)时,会立即调用 parseRequest() 方法对请求数据进行解析,然后将解析结果封装到 DefaultMultipartHttpServletRequest中;而当resolveLazily为 true时,会在DefaultMultipartHttpServletRequest的initializeMultipart()方法调用parseRequest()方法对请求数据进行解析,而initializeMultipart()方法又是被getMultipartFiles()方法调用,即当需要获取文件信息时才会去解析请求数据,这种方式用了懒加载的思想。

再次修改代码:

1、在springmvc-config.xml增加一个配置resolveLazily,设置true(如何配置见文末);

2、将controller方法中将@RequestParam("file") MultipartFile file注释

再次测试,发现还是不对。。增加打印日志后发现:

@RequestMapping(value="/upload",method=RequestMethod.POST)
public String upload(HttpServletRequest request,
@RequestParam("description") String description
/*@RequestParam("file") MultipartFile file*/) throws Exception {
System.out.println("enter controller."); // 还是文件上传完后,才打印
System.out.println(request.getHeader("Accept"));
System.out.println(request.getParam("description"));
// 省略获取上传文件的逻辑
}

再注释@RequestParam("description") String description

@RequestMapping(value="/upload",method=RequestMethod.POST)
public String upload(HttpServletRequest request
/*@RequestParam("description") String description, */
/*@RequestParam("file") MultipartFile file*/) throws Exception {
System.out.println("enter controller."); // 页面提交后,立刻打印
System.out.println(request.getHeader("Accept")); // 几乎也是立刻打印
System.out.println(request.getParam("description")); // 文件上传完,才打印
// 省略获取上传文件的逻辑
}

这次,第1,2条日志很快打印,第3条日志仍是文件上传完后才打印。简单分析下,应该是后台一直在接收数据,HTTP首部很快就能获取到并解析出来(首部和正文之间有一个空行),但请求正文部分必须在请求数据完全接收后才能解析,因此线程一直阻塞直到数据接收完成才打印第3条日志。因此解决方案是,把controller函数中的@RequestParam("description") String description也注释掉,这样页面提交后,会立即进入controller中。

总结下最终的解决方案:

1、springmvc-config.xml增加一项配置:

<property name="resolveLazily ">
  <value>true</value>
</property>

2、在controller的方法参数中,不能附加请求参数,应在函数中自己解析参数。

Spring MVC上传文件原理和resolveLazily说明的更多相关文章

  1. Spring MVC上传文件

    Spring MVC上传文件 1.Web.xml中加入 <servlet> <servlet-name>springmvc</servlet-name> <s ...

  2. Spring MVC 上传文件

    Spring MVC上传文件需要如下步骤: 1.前台页面,form属性 method设置为post,enctype="multipart/form-data"  input的typ ...

  3. 解析Spring MVC上传文件

    新建一个普通的maven工程 在pom.xml文件中引入相应的坐标 <?xml version="1.0" encoding="UTF-8"?> & ...

  4. spring MVC上传文件演示

    //相比smartUpload功能上感觉确实有点心有意力不足的感觉,就安全性判断后缀,smartUpload就非常方便. public ModelAndView addFileUp(HttpServl ...

  5. Spring Mvc 上传文件Demo 实例

    返得利购物. 淘宝.京东500家商城合作,包括全面的商城返利网.注冊就送5元,购物就有返利.随时提现. 同学们,新一轮的返利大潮正在慢慢靠近,让购物都认为自己在赚钱.购物,机票.游戏.酒店旅游,地方特 ...

  6. Spring框架学习(8)spring mvc上传下载

    内容源自:spring mvc上传下载 如下示例: 页面: web.xml: <?xml version="1.0" encoding="UTF-8"?& ...

  7. MVC上传文件

    ASP.NET MVC上传文件是必段撑握的知识.加强训练才是.以前Insus.NET曾使用第三方MyAjaxForm.js :http://www.cnblogs.com/insus/p/378548 ...

  8. springboot(十七):使用Spring Boot上传文件

    上传文件是互联网中常常应用的场景之一,最典型的情况就是上传头像等,今天就带着带着大家做一个Spring Boot上传文件的小案例. 1.pom包配置 我们使用Spring Boot最新版本1.5.9. ...

  9. asp.net MVC 上传文件 System.Web.HttpException: 超过了最大请求长度

    APS.NET MVC 上传文件出现  System.Web.HttpException: 超过了最大请求长度 这个问题 原因是 默认最大上传文件大小为4096,而我提交的文件太大了. 解决方案:修改 ...

随机推荐

  1. ASCALL码对照表

    ASCALL码对照表 目前计算机中用得最广泛的字符集及其编码,是由美国国家标准局(ANSI)制定的ASCII码(American Standard Code for Information Inter ...

  2. cmd命令查看Python模块函数等帮助文档和介绍

    dir函数式可以查看对象的属性 使用方法很简单,举os类型为例,在Python命令窗口输入 dir(‘os’) 即可查看os模块的属性 打开cmd命令窗口 ​ 输入python(注意:计算机需要有Py ...

  3. JS面试题-<变量和类型>-JavaScript浅拷贝与深拷贝

    前言 最开始了解到深浅拷贝是因为准备面试,但那个时候因为在学校做的项目比较少需求也比较简单,所以没有在项目中遇到这类问题,所以对这个问题就属于知道这个知识点,看过相关内容,却没有自己的总结,也没有深入 ...

  4. C语言程序设计100例之(8):尼科彻斯定理

    例8    尼科彻斯定理 题目描述 尼科彻斯定理可以叙述为:任何一个整数的立方都可以表示成一串连续的奇数的和.需要注意的是,这些奇数一定是连续的,如:1,3,5,7,9,…. 例如,对于整数5,5*5 ...

  5. 项目——基于httpd镜像演示Dockerfile所有的指令

    基于httpd镜像演示Dockerfile所有的指令: 第一步:创建Dockerfile工作目录 [root@localhost harbor]# mkdir /test [root@localhos ...

  6. vue2获取dom节点

    vue2.*版本中 在标签中加上ref='dom',然后在代码中this.$refs.dom这样就拿到了页面元素 例如:<div class='box' ref='myBox'>你好< ...

  7. Java 读取Word批注中的文本和图片

    本文将介绍读取Word批注的方法,包括读取Word批注中的文本及图片.关于操作Word批注的方法还可以参考这两篇文章:Java 添加.回复.修改.删除Word批注:Java 给Word指定字符串添加批 ...

  8. Ubuntu 16.04 安装Docker

    1 更改apt源,更改前先对sources.list文件进行备分 ccskun@test:~$ sudo cp /etc/apt/sources.list /etc/apt/sources.list. ...

  9. Leetcode 25/24 - Reverse Nodes in k-Group

    题目描述 Leetcode 24 题主要考察的链表的反转,而 25 题是 24 的拓展版,加上对递归的考察. 对题目做一下概述: 提供一个链表,给定一个正整数 k, 每 k 个节点一组进行翻转,最后返 ...

  10. Vue使用element上传

    <el-upload action v-if="IsUpload" style="display:inline" list-type="pict ...