SpringMVC上传文件的 4 种方式,你都会么?

1、本文内容

  • 文件上传开发步骤
  • 单文件上传
  • 多文件上传
  • 通过 MultipartHttpServletRequest 处理文件上传
  • 通过自定义对象接收上传的文件
  • 扩展知识
  • 案例代码

2、预备知识

springmvc 系列中的测试案例,基本上都是使用 idea 中的 http client 插件开发的,是一款非常高效的接口测试工具,还没有用过的,建议先去了解下用法:idea 中的接口测试利器(http cient 插件)

3、软件版本

  • idea 2020.3.3
  • jdk1.8
  • ≥maven3.6.1
  • spring5.3.6
  • apache-tomcat-9.0.46

4、springmvc 文件上传步骤

4 个步骤

1、添加 maven 配置
2、springmvc 容器中定义 MultipartResolver 这个 bean
3、controller 中使用 MultipartFile 接收上传的文件
4、调用 MultipartFile#transferTo 方法保存文件
5、指定请求类型为:multipart/form-data

步骤 1:添加 maven 配置

<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.4</version>
</dependency>
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>

步骤 2:定义 MultipartResolver 这个 bean

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">   
    <property name="maxUploadSizePerFile" value="#{10*1024*1024}"/>
    <property name="maxUploadSize" value="#{100*1024*1024}"/>
</bean>
http 上传的请求类型为 multipart/form-data 类型,springmvc 内部需要为这种请求指定解析器,解析器的类型为org.springframework.web.multipart.MultipartResolver
MultipartResolver 有 2 个实现类,这里我们使用 CommonsMultipartResolver 作为解析器来解析文件上传的 http 请求
注意 bean 名称必须为multipartResolver
两个比较有用的属性
maxUploadSizePerFile:单个文件大小限制(byte)
maxUploadSize:整个请求大小限制(byte)

步骤 3:控制器使用 MultipartFile 接收上传的文件

@RequestMapping("/upload1.do")
public ModelAndView upload1(@RequestParam("file1") MultipartFile f1){
}

步骤 4:调用 MultipartFile#transferTo 方法保存文件

@RequestMapping("/upload1.do")
public ModelAndView upload1(@RequestParam("file1") MultipartFile f1){
 //destFile为目标文件,即将上传的文件写到destFile中
 f1.transferTo(destFile);
}

步骤 5:设置 http 请求类型为 multipart/form-data

上传文件,需要设置 form 表单的 enctype 属性值为 multipart/form-data

下面来介绍 4 种常用的上传文件的方式。

5、MultipartFile:上传的文件对象

springmvc 中使MultipartFile这个类来表示上传的文件,提供了一系列方法来获取上传的文件的信息。

方法描述String getName()用来获取中 name 的名称String getOriginalFilename()获取文件的原始名称String getContentType()获取文件类型long getSize()获取文件大小(byte)byte[] getBytes()获取文件内容InputStream getInputStream()获取文件流void transferTo(File dest)将上传的文件写到 dest 中

6、单文件上传

控制器中使用一个 MultipartFile 来接收上传的文件,下面看代码。

表单代码

<form method="post" action="upload1.do" enctype="multipart/form-data">
    文件:<input type="file" name="file1"><br/>
    <input type="submit" value="提交">
</form>
表单中文件元素的 name 为 file1

控制器代码

/**
 * 单文件上传
 * 1、MultipartFile用来接收表单中上传的文件
 * 2、每个MultipartFile对应表单中的一个元素
 * 3、@RequestParam("f1")用来自动接受表单中的哪个元素?value用来指定表单元素的名称
 *
 * @param f1
 * @return
 * @throws IOException
 */
@RequestMapping("/upload1.do")
public ModelAndView upload1(@RequestParam("file1") MultipartFile f1) throws IOException {
    //获取文件名称
    String originalFilename = f1.getOriginalFilename();
    String destFilePath = String.format("E:\\idea\\springmvc-series\\chat04-uploadfile\\src\\main\\webapp\\upfile\\%s", originalFilename);
    File destFile = new File(destFilePath);
    //调用transferTo将上传的文件保存到指定的地址
    f1.transferTo(destFile);

    ModelAndView modelAndView = new ModelAndView();
    modelAndView.setViewName("/WEB-INF/view/result.jsp");
    modelAndView.addObject("msg", destFile.getAbsolutePath());
    return modelAndView;
}
上传的文件会被传入 update1 方法的第一个参数,注意第一个参数有个@RequestParam("file1")注解,这个注解的 value 就是表单中文件元素名称。

7、多文件上传

当上传多个文件的时候,可以使用多个 MultipartFile 参数来接收上传的文件。

表单代码

<form method="post" action="upload2.do" enctype="multipart/form-data">
    文件1:<input type="file" name="file1"><br/>
    文件2:<input type="file" name="file2"><br/>
    <input type="submit" value="提交">
</form>

控制器代码

/**
 * 多文件上传
 * 1、方法中指定多个MultipartFile,每个MultipartFile对应一个上传的文件
 * 2、@RequestParam("file1") 用来指定具体接受上传的表单中哪个元素的名称
 *
 * @param f1
 * @param f2
 * @return
 */
@RequestMapping("/upload2.do")
public ModelAndView upload2(@RequestParam("file1") MultipartFile f1,
                            @RequestParam("file2") MultipartFile f2) {
    System.out.println("f1:" + f1);
    System.out.println("f2:" + f2);
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.setViewName("/WEB-INF/view/result.jsp");
    modelAndView.addObject("msg", null);
    return modelAndView;
}

我们上传 2 个图片,控制台输出

f1:MultipartFile[field="file1", filename=1.jpg, contentType=image/jpeg, size=145615]
f2:MultipartFile[field="file2", filename=2.jpg, contentType=image/jpeg, size=67713]

8、通过 MultipartHttpServletRequest 处理文件上传

MultipartHttpServletRequest 接口

1、springmvc 接受到上传文件的的请求之后,会将请求转换为 MultipartHttpServletRequest 类型的对象
2、MultipartHttpServletRequest 中提供了一系列方法来获取请求中的所有参数信息
3、其中 getParameterMap()用来获取非文件类型的参数列表
4、getMultiFileMap()方法用来获取上传的文件列表

下面来个案例,使用 MultipartHttpServletRequest 来处理上传的文件请求。

表单代码

下面表单中有 2 个文本,2 个文件元素。
<form method="post" action="upload3.do" enctype="multipart/form-data">
    姓名:<input name="name" value="路人"/> <br/>
    年龄:<input name="age" value="30"/><br/>
    文件1:<input type="file" name="file1"><br/>
    文件2:<input type="file" name="file2"><br/>
    <input type="submit" value="提交">
</form>

控制器代码

控制器中使用 MultipartHttpServletRequest 来获取所有参数信息,分了 2 部分获取
1、先使用 request.getParameterMap()获取非文件类型的参数,即可以获取表单中的 name 和 age 这 2 个参数的信息
2、通过 request.getMultiFileMap()获取文件类型的参数,即可以获取表单中 file1 和 file2 这 2 个文件的信息
稍后关注控制台的输出
/**
 * 使用MultipartHttpServletRequest处理多文件上传
 * 上传文件的http请求会被转换为MultipartHttpServletRequest类型
 * MultipartHttpServletRequest中提供了很多很多方法用来获取请求中的参数
 *
 * @param request
 * @return
 */
@RequestMapping("/upload3.do")
public ModelAndView upload3(MultipartHttpServletRequest request) {
    //1.获取表单中非文件数据
    System.out.println("---------获取表单中非文件数据---------");
    Map parameterMap = request.getParameterMap();
    parameterMap.forEach((name, values) -> {
        System.out.println(String.format("%s:%s", name, Arrays.asList(values)));
    });
    //2、获取表单中文件数据
    System.out.println("---------获取表单中文件数据---------");
    MultiValueMap multiFileMap = request.getMultiFileMap();
    //2、遍历表单中元素信息
    multiFileMap.forEach((name, files) -> {
        System.out.println(String.format("%s:%s", name, files));
    });

    ModelAndView modelAndView = new ModelAndView();
    modelAndView.setViewName("/WEB-INF/view/result.jsp");
    modelAndView.addObject("msg", "上传成功");
    return modelAndView;
}

使用 http client 发起请求

这里我们使用 idea 中的 http client 这个插件来调用接口,http client 插件是一个非常方便发起 http 请求额一个插件,测试接口特别容易,后面的大量案例中我们都会使用这个工具来快速测试接口的效果,所以这个工具,如果还不会的,建议去看一下这篇文章:

idea 中的接口测试利器(http cient 插件)

下面,我们在 idea 中创建一个 UploadController.http 文件,文件内容如下:

### 多文件上传
POST http://localhost:8080/chat04/upload3.do
Content-Type: multipart/form-data; boundary=bound1

--bound1
Content-Disposition: form-data; name="name"

路人
--bound1
Content-Disposition: form-data; name="age"

30
--bound1
Content-Disposition: form-data; name="file1"; filename="1.jpg"

< ./1.jpg
--bound1
Content-Disposition: form-data; name="file2"; filename="2.jpg"
Content-Type: image/jpeg

< ./2.jpg
--bound1--

控制台输出

---------获取表单中非文件数据---------
name:[路人]
age:[30]
---------获取表单中文件数据---------
file1:[MultipartFile[field="file1", filename=1.jpg, contentType=*/*; charset=UTF-8, size=145615]]
file2:[MultipartFile[field="file2", filename=2.jpg, contentType=image/jpeg, size=67713]]

9、自定义对象接收多文件上传

表单代码

这个表单用来输入用户信息:
姓名、年龄、头像图片、2 张身份证图片
<form method="post" action="upload4.do" enctype="multipart/form-data">
    姓名:<input name="name" value="路人"/> <br/>
    年龄:<input name="age" value="30"/><br/>
    头像图片:<input name="headImg" type="file"/><br/>
    多张身份证图片<br/>
    <input name="idCardImg" type="file"/><br/>
    <input name="idCardImg" type="file"/><br/>
    <input type="submit" value="提交">
</form>

自定义一个类

自定义了一个 UserDto,来接收上面表单的参数。
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

public class UserDto {
    //姓名
    private String name;
    //年龄
    private Integer age;
    //头像
    private MultipartFile headImg;
    //身份证(多张图像)
    private List idCardImg;

    //省略了get、set方法...
}

控制器代码

@RequestMapping("/upload4.do")
public ModelAndView upload4(UserDto userDto) {
    System.out.println("姓名:" + userDto.getName());
    System.out.println("年龄:" + userDto.getAge());
    System.out.println("头像文件:" + userDto.getHeadImg());
    System.out.println("多张身份证文件:" + Arrays.asList(userDto.getIdCardImg()));

    ModelAndView modelAndView = new ModelAndView();
    modelAndView.setViewName("/WEB-INF/view/result.jsp");
    modelAndView.addObject("msg", "上传成功");
    return modelAndView;
}

测试代码

这里我们还是使用 http client 插件发起请求
### 多文件上传
POST http://localhost:8080/chat04/upload4.do
Content-Type: multipart/form-data; boundary=bound1

--bound1
Content-Disposition: form-data; name="name"
Content-Type: text/plain

路人
--bound1
Content-Disposition: form-data; name="age"
Content-Type: text/plain

30
--bound1
Content-Disposition: form-data; name="headImg"; filename="1.jpg"

< ./1.jpg
--bound1
Content-Disposition: form-data; name="idCardImg"; filename="2.jpg"
Content-Type: image/jpeg

< ./2.jpg
--bound1
Content-Disposition: form-data; name="idCardImg"; filename="3.jpg"
Content-Type: image/jpeg

< ./3.jpg
--bound1--

控制台输出

输出如下,可以看到 UserDto 这个对象中的详细信息
姓名:路人
年龄:30
头像文件:MultipartFile[field="headImg", filename=1.jpg, contentType=*/*; charset=UTF-8, size=145615]
多张身份证文件:[[MultipartFile[field="idCardImg", filename=2.jpg, contentType=image/jpeg, size=67713], MultipartFile[field="idCardImg", filename=3.jpg, contentType=image/jpeg, size=39891]]]

12、扩展

MultipartResolver 这个 bean 的名称为什么必须是 multipartResolver?

springmvc 中会使用 MultipartResolver 来解析上传文件的请求,具体代码在org.springframework.web.servlet.DispatcherServlet#doDispatch中

进入checkMultipart,如下,这里面关键代码就是会调用this.multipartResolver.resolveMultipart(request)来处理文件上传的请求

下面看一下 this.multipartResolver 从哪里来的,如下,是从 springmvc 容器中查找的,名称为multipartResolver,所以我们定义这个 bean 的时候,名称必须为这个。

13、案例代码如何运行?

step1:拉取代码

https://gitee.com/javacode2018/springmvc-series

step2:导入 idea

step3:代码结构

本文的案例在chat04-uploadfile模块中。

页面

本文的表单代码都在 index.jsp 中

控制器

本文的控制器代码都在 UploadController 类中,如下

http client 测试案例代码

如下图,http client 测试案例代码都在 UploadController.http 文件中,包含了 4 个案例的测试代码,大家可以点击之后直接运行。

step4:下载 tomcat

链接:https://pan.baidu.com/s/1_Ol-UZkN_6woMBtjcFygvQ
提取码:e66j

step5:将 chat04-uploadfile 部署到 tomcat

step6:启动 tomcat,验证效果

14、SpringMVC 系列

  1. SpringMVC 系列第 1 篇:helloword
  2. SpringMVC 系列第 2 篇:@Controller、@RequestMapping
  3. SpringMVC 系列第 3 篇:异常高效的一款接口测试利器
  4. SpringMVC 系列第 4 篇:controller 常见的接收参数的方式
  5. SpringMVC 系列第 5 篇:@RequestBody 大解密,说点你不知道的

15、更多好文章

  1. Spring 高手系列(共 56 篇)
  2. Java 高并发系列(共 34 篇)
  3. MySql 高手系列(共 27 篇)
  4. Maven 高手系列(共 10 篇)
  5. Mybatis 系列(共 12 篇)
  6. 聊聊 db 和缓存一致性常见的实现方式
  7. 接口幂等性这么重要,它是什么?怎么实现?
  8. 泛型,有点难度,会让很多人懵逼,那是因为你没有看这篇文章!

各位小伙伴,如果本文对你有帮助,不要光看,记得点赞加关注,这对我来说,非常非常重要,也是我继续做下去的一个动力。

  • 8
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值