18.Spring MVC文件上传
在实际的项目开发中,文件的上传和下载可以说是最常用的功能之一,例如图片的上传与下载、邮件附件的上传和下载等。本节我们将对 Spring MVC 中的文件上传功能进行讲解。
在 Spring MVC 中想要实现文件上传工作,需要的步骤如下。
1. 编写 form 表单
在 Spring MVC 项目中,大多数的文件上传功能都是通过 form 表单提交到后台服务器的。
form 表单想要具有文件上传功能,其必须满足以下 3 个条件。
- form 表单的 method 属性必须设置为 post。
- form 表单的 enctype 属性设置为 multipart/form-data。
- 至少提供一个 type 属性为 file 的 input 输入框。
常见的文件上传表单示例代码如下。
- <form action="/upload" method="post" enctype="multipart/form-data">
- <input type="file" name="fileName" multiple="multiple"/>
- <input type="submit" value="上传">
- </form>
当 form 表单的 enctype 属性为 multipart/form-data 时,浏览器会以二进制流的方式对表单数据进行处理,由服务端对文件上传的请求进行解析和处理。
在上面的代码中,除了满足文件上传表单所必须具备的 3 个条件外,<input> 标签中还增加了一个 multiple 属性。该属性可以让我们同时选择对个文件进行上传,即实现多文件上传功能。
2. 配置文件解析器(MultipartResolver )
Spring MVC 提供了一个名为 MultipartResolver 的文件解析器,来实现文件上传功能。MultipartResolver 本身是一个接口,我们需要通过它的实现类来完成对它的实例化工作。
MultipartResolver 接口共有两个实现类,如下表。
实现类 | 说明 | 依赖 | 支持的 Servlet 版本 |
---|---|---|---|
StandardServletMultipartResolver | 它是 Servlet 内置的上传功能。 | 不需要第三方 JAR 包的支持。 | 仅支持 Servlet 3.0 及以上版本 |
CommonsMultipartResolver | 借助 Apache 的 commons-fileupload 来完成具体的上传操作。 | 需要 Apache 的 commons-fileupload 等 JAR 包的支持。 | 不仅支持 Servlet 3.0 及以上版本,还可以在比较旧的 Servlet 版本中使用。 |
以上这两个 MultipartResolver 的实现类,无论使用哪一个都可以实现 Spring MVC 的文件上传功能。这里,我们以 CommonsMultipartResolver 为例进行讲解。
想要在 Spring MVC 中使用 CommonsMultipartResolver 对象实现文件上传,我们需要在 Spring MVC 的配置文件中对其进行以下配置。
- <!--配置文件上传解析器-->
- <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
- <property name="defaultEncoding" value="UTF-8"></property>
- <property name="maxUploadSize" value="1024000"></property>
- </bean>
在以上配置中,除了定义了 CommonsMultipartResolver 的 Bean 外,还通过 <property> 标签对文件的编码格式和上传文件的大小进行了配置。
通过 <property> 可以对 CommonsMultipartResolver 的多个属性进行配置,其中常用的属性如下表。
属性 | 说明 |
---|---|
defaultEncoding | 上传文件的默认编码格式。 |
maxUploadSize | 上传文件的最大长度(单位为字节)。 |
maxInMemorySize | 读取文件到内存中的最大字节数。 |
resolveLazily | 判断是否要延迟解析文件。 |
注意:当我们在 Spring MVC 的配置文件中对 CommonsMultipartResolver 的 Bean 进行定义时,必须指定这个 Bean 的 id 为 multipartResolver,否则就无法完成文件的解析和上传工作。
3. 引入 Jar 包
由于 CommonsMultipartResolver 是 Spring MVC 内部通过 Apache Commons FileUpload 技术实现的,因此我们还需要将 Apache Commons FileUpload 组件的相关依赖引入到项目中。
注:点击上面的链接,即可下载相应的 JAR 包。
4. 编写控制器方法
在完成上面的所有步骤后,接下来,我们只需要在 Controller 中编写文件上传的方法即可实现文件的上传。
- @Controller
- public class FileUploadController {
- @RequestMapping("/uplaod")
- public String upload(MultipartFile file) {
- if (!file.isEmpty()) {
- return "success";
- }
- return "error";
- }
- }
在该控制器方法中包含一个 org.springframework.web.multipart.MultipartFile 接口类型的形参,该参数用来封装被上传文件的信息。MultipartFile 接口是 InputStreamSource 的子接口,该接口中提供了多个不同的方法,如下表。
名称 | 作用 |
---|---|
byte[] getBytes() | 以字节数组的形式返回文件的内容。 |
String getContentType() | 返回文件的内容类型。 |
InputStream getInputStream() | 返回一个 input 流,从中读取文件的内容。 |
String getName() | 返回请求参数的名称。 |
String getOriginalFillename() | 返回客户端提交的原始文件名称。 |
long getSize() | 返回文件的大小,单位为字节。 |
boolean isEmpty() | 判断被上传文件是否为空。 |
void transferTo(File destination) | 将上传文件保存到目标目录下。 |
上传文件示例
下面,我们就通过一个实例,来演示下如何在 Spring MVC 中上传文件,具体步骤如下。
1. 新建一个名为 springmvc-file-demo 的 Web 工程,并将 Spring MVC 以及 Apache Commons FileUpload 相关的依赖导入到该工程中,web.xml 配置如下。
- <?xml version="1.0" encoding="UTF-8"?>
- <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
- version="4.0">
- <!--请求和响应的字符串过滤器-->
- <filter>
- <filter-name>CharacterEncodingFilter</filter-name>
- <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
- <init-param>
- <param-name>encoding</param-name>
- <param-value>UTF-8</param-value>
- </init-param>
- <init-param>
- <param-name>forceResponseEncoding</param-name>
- <param-value>true</param-value>
- </init-param>
- </filter>
- <filter-mapping>
- <filter-name>CharacterEncodingFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
- <!--来处理 PUT 和 DELETE 请求的过滤器-->
- <filter>
- <filter-name>HiddenHttpMethodFilter</filter-name>
- <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>HiddenHttpMethodFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
- <!-- 配置SpringMVC的前端控制器,对浏览器发送的请求统一进行处理 -->
- <servlet>
- <servlet-name>dispatcherServlet</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <init-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>classpath:springMVC.xml</param-value>
- </init-param>
- <load-on-startup>1</load-on-startup>
- </servlet>
- <servlet-mapping>
- <servlet-name>dispatcherServlet</servlet-name>
- <url-pattern>/</url-pattern>
- </servlet-mapping>
- </web-app>
2. 在 src 目录下新建一个 Spring MVC 的配置文件 springMVC.xml,配置内容如下。
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:mvc="http://www.springframework.org/schema/mvc"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans.xsd
- http://www.springframework.org/schema/context
- https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
- <!--开启组件扫描-->
- <context:component-scan base-package="net.biancheng.c"></context:component-scan>
- <!-- 配置 Thymeleaf 视图解析器 -->
- <bean id="viewResolver"
- class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
- <property name="order" value="1"/>
- <property name="characterEncoding" value="UTF-8"/>
- <property name="templateEngine">
- <bean class="org.thymeleaf.spring5.SpringTemplateEngine">
- <property name="templateResolver">
- <bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
- <!-- 视图前缀 -->
- <property name="prefix" value="/WEB-INF/templates/"/>
- <!-- 视图后缀 -->
- <property name="suffix" value=".html"/>
- <property name="templateMode" value="HTML5"/>
- <property name="characterEncoding" value="UTF-8"/>
- </bean>
- </property>
- </bean>
- </property>
- </bean>
- <!--当SpringMVC中设置任何一个view-controller时,其他控制器中的请求映射将全部失效,此时需要在SpringMVC的核心配置文件中设置开启mvc注解驱动的标签-->
- <mvc:annotation-driven></mvc:annotation-driven>
- <!--配置文件上传解析器-->
- <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
- <!--设置上传文件的默认编码格式-->
- <property name="defaultEncoding" value="UTF-8"></property>
- <!--设置允许上传的最大长度-->
- <property name="maxUploadSize" value="1024000"></property>
- </bean>
- <!--访问"/" 跳转到 file-upload.html 中-->
- <mvc:view-controller path="/" view-name="file-upload"></mvc:view-controller>
- <mvc:default-servlet-handler></mvc:default-servlet-handler>
- </beans>
3. 在 net.biancheng.c.entity 包下,创建一个名为 Student 的类,代码如下。
- package net.biancheng.c.entity;
- import org.springframework.web.multipart.MultipartFile;
- import java.util.List;
- public class Student {
- //学号
- private String stuId;
- //姓名
- private String stuName;
- //年龄
- private Integer age;
- //用于接收后台上传的文件
- private List<MultipartFile> photos;
- //文件名称的字符串
- private String fileNameStr;
- //已上传图片的路径集合
- private List<String> path;
- public List<String> getPath() {
- return path;
- }
- public void setPath(List<String> path) {
- this.path = path;
- }
- public void setPhotos(List<MultipartFile> photos) {
- this.photos = photos;
- }
- public String getFileNameStr() {
- return fileNameStr;
- }
- public void setFileNameStr(String fileNameStr) {
- this.fileNameStr = fileNameStr;
- }
- public String getStuId() {
- return stuId;
- }
- public void setStuId(String stuId) {
- this.stuId = stuId;
- }
- public String getStuName() {
- return stuName;
- }
- public void setStuName(String stuName) {
- this.stuName = stuName;
- }
- public Integer getAge() {
- return age;
- }
- public void setAge(Integer age) {
- this.age = age;
- }
- public List<MultipartFile> getPhotos() {
- return photos;
- }
- @Override
- public String toString() {
- return "Student{" +
- "stuId='" + stuId + '\'' +
- ", stuName='" + stuName + '\'' +
- ", age=" + age +
- ", photos=" + photos +
- ", photoPath='" + fileNameStr + '\'' +
- ", path=" + path +
- '}';
- }
- }
4. 在 webapp 下新建一个 js 目录,并将 jquery-3.6.0.min.js 添加进该目录下。
5. 在 webapp/WEB-INF 下新建一个 templates 目录,并在该目录下创建一个 file-upload.html ,代码如下。
- <!DOCTYPE html>
- <html lang="en" xmlns:th="http://www.thymeleaf.org">
- <head>
- <meta charset="UTF-8">
- <title>Title</title>
- <!--引入 jquery-->
- <script type="text/javaScript"
- src="../../js/jquery-3.6.0.min.js" th:src="@{/js/jquery-3.6.0.min.js}"></script>
- </head>
- <body>
- <form th:action="@{/student}" method="post" enctype="multipart/form-data">
- <table style="margin: auto">
- <tr>
- <td th:if="${not #strings.isEmpty(msg)}" colspan="2" align="center">
- <p style="color: red;margin: auto" th:text="${msg}"></p>
- </td>
- </tr>
- <tr>
- <td>学号:</td>
- <td><input type="text" name="stuId" required><br></td>
- </tr>
- <tr>
- <td>学生姓名:</td>
- <td><input type="text" name="stuName" required><br></td>
- </tr>
- <tr>
- <td>年龄:</td>
- <td><input type="number" name="age" required><br></td>
- </tr>
- <tr>
- <td>照片:</td>
- <td><input type="file" id="chooseImage" name="photos" multiple="multiple" required><br>
- <span id="img-div"></span></td>
- </tr>
- <input id="fileNameStr" type="hidden" name="fileNameStr"/>
- <tr>
- <td colspan="2" align="center">
- <input type="submit" value="提交">
- <input type="reset" value="重置">
- </td>
- </tr>
- </table>
- <!-- 保存用户自定义的背景图片 -->
- <img id="preview_photo" src="" width="200px" height="200px">
- </form>
- <script type="text/javascript" th:inline="javascript">
- /*<![CDATA[*/
- ctxPath = /*[[@{/}]]*/ '';
- /*]]>*/
- </script>
- <script type="text/javaScript">
- $('#chooseImage').on('change', function () {
- var filePath = $(this).val(), //获取到input的value,里面是文件的路径
- fileFormat = filePath.substring(filePath.lastIndexOf(".")).toLowerCase();
- // 检查是否是图片
- if (!fileFormat.match(/.png|.jpg|.jpeg/)) {
- alert('上传错误,文件格式必须为:png/jpg/jpeg');
- return;
- }
- //获取上传的文件
- var arr = document.getElementById('chooseImage').files;
- //遍历文件
- for (var i = 0; i < arr.length; i++) {
- //通过 FormData 将文件信息提交到后台
- var formData = new FormData();
- formData.append('photo', arr[i]);
- $.ajax({
- url: "http://localhost:8080/springmvc-file-demo/uploadPhoto",
- type: "post",
- data: formData,
- contentType: false,
- processData: false,
- success: function (data) {
- if (data.type == "success") {
- //在图片显示区显示图片
- var html = "<img id='" + data.filename + "' src='" + ctxPath + data.filepath + data.filename + "' width='200px' height='200px'> ";
- $("#img-div").append(html);
- //将文件路径赋值给 fileNameStr
- var path = $("#fileNameStr").val();
- if (path == "") {
- $("#fileNameStr").val(data.filename);
- } else {
- $("#fileNameStr").val(path + "," + data.filename);
- }
- } else {
- alert(data.msg);
- }
- },
- error: function (data) {
- alert("上传失败")
- }
- });
- }
- });
- </script>
- <style>
- img[src=""], img:not([src]) {
- opacity: 0;
- }
- </style>
- </body>
- </html>
6. 在 net.biancheng.c.controller 包下,创建一个名为 MultiFileController 的控制器类,代码如下。
- package net.biancheng.c.controller;
- import net.biancheng.c.entity.Student;
- import org.apache.commons.io.FileUtils;
- import org.springframework.http.HttpHeaders;
- import org.springframework.http.HttpStatus;
- import org.springframework.http.MediaType;
- import org.springframework.http.ResponseEntity;
- import org.springframework.stereotype.Controller;
- import org.springframework.ui.Model;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RequestMethod;
- import org.springframework.web.bind.annotation.ResponseBody;
- import org.springframework.web.multipart.MultipartFile;
- import javax.servlet.http.HttpServletRequest;
- import java.io.File;
- import java.io.IOException;
- import java.util.*;
- @Controller
- public class MultiFileController {
- /**
- * 图片上传
- *
- * @param photo
- * @param request
- * @return
- */
- @RequestMapping(value = "/uploadPhoto", method = RequestMethod.POST)
- @ResponseBody
- public Map<String, String> uploadPhoto(MultipartFile photo, HttpServletRequest request) {
- Map<String, String> ret = new HashMap<String, String>();
- if (photo == null) {
- ret.put("type", "error");
- ret.put("msg", "选择要上传的文件!");
- return ret;
- }
- if (photo.getSize() > 1024 * 1024 * 10) {
- ret.put("type", "error");
- ret.put("msg", "文件大小不能超过10M!");
- return ret;
- }
- //获取文件后缀
- String suffix = photo.getOriginalFilename().substring(photo.getOriginalFilename().lastIndexOf(".") + 1, photo.getOriginalFilename().length());
- if (!"jpg,jpeg,gif,png".toUpperCase().contains(suffix.toUpperCase())) {
- ret.put("type", "error");
- ret.put("msg", "请选择jpg、peg、gif、png 格式的图片!");
- return ret;
- }
- String realPath = request.getServletContext().getRealPath("/upload/");
- System.out.println(realPath);
- File fileDir = new File(realPath);
- if (!fileDir.exists()) {
- fileDir.mkdir();
- }
- String filename = photo.getOriginalFilename();
- System.err.println("正在上传的图片为:" + filename);
- String newFileName = UUID.randomUUID() + filename;
- try {
- //将文件保存指定目录
- photo.transferTo(new File(realPath + newFileName));
- } catch (Exception e) {
- ret.put("type", "error");
- ret.put("msg", "保存文件异常!");
- e.printStackTrace();
- return ret;
- }
- ret.put("type", "success");
- ret.put("msg", "上传图片成功!");
- ret.put("filepath", "/upload/");
- ret.put("filename", newFileName);
- return ret;
- }
- /**
- * 提交学生信息
- *
- * @param student
- * @param model
- * @return
- */
- @RequestMapping(value = "/student", method = RequestMethod.POST)
- public String uploadFile(Student student, Model model) {
- String fileNameStr = student.getFileNameStr();
- //将图片路径的字符串拆分,添加到图片路径的集合中
- String[] split = fileNameStr.split(",");
- List<String> list = new ArrayList<>();
- for (String fileName : split) {
- list.add(fileName);
- }
- student.setPath(list);
- model.addAttribute("student", student);
- return "success";
- }
- }
5. 在 webapps/WEB-INF/tempaltes 目录下,新建一个 success.html,代码如下。
- <!DOCTYPE html>
- <html lang="en" xmlns:th="http://www.thymeleaf.org">
- <head>
- <meta charset="UTF-8">
- <title>Title</title>
- </head>
- <body>
- <h1>学生信息上传成功</h1>
- <table>
- <tr>
- <td>学号:</td>
- <td th:text="${student.getStuId()}"></td>
- </tr>
- <tr>
- <td>姓名:</td>
- <td th:text="${student.getStuName()}"></td>
- </tr>
- <tr>
- <td>年龄:</td>
- <td th:text="${student.getAge()}"></td>
- </tr>
- <tr>
- <td>照片:</td>
- <td th:each="p:${student.getPath()}">
- <img th:src="{p}" width='200px' height='200px'/><br>
- </td>
- </tr>
- </table>
- </body>
- </html>
6. 将 springmvc-file-demo 部署到 Tomcat 服务器中并启动该服务器,使用浏览器访问“http://localhost:8080/springmvc-file-demo/”,结果如下图。

7. 在表单中分别填写学号、姓名、年龄等信息,然后点击“选择文件”按钮,如下图。

8. 选择需要上传的一张或多张图片,选择完成后,页面中回显了上传的图片,如下图。

9. 点击下方的提交按钮,将学生信息提交到后台,结果如下图。

图4:学生信息提交成功
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理