本文目录导读:

- 项目依赖配置 (pom.xml)
- 配置Spring MVC (web.xml)
- Spring MVC配置文件 (spring-mvc.xml)
- 文件上传Controller (FileUploadController.java)
- 文件上传页面 (upload.jsp)
- 上传结果页面 (result.jsp)
- 项目结构
- 配置文件上传大小限制
- 注意事项
我将为您提供一个完整的Spring MVC文件上传案例,包含单文件上传和多文件上传。
项目依赖配置 (pom.xml)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>file-upload-demo</artifactId>
<version>1.0.0</version>
<packaging>war</packaging>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- Spring MVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.20</version>
</dependency>
<!-- Servlet API -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- JSP API -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
<!-- JSTL -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- 文件上传依赖 -->
<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.11.0</version>
</dependency>
</dependencies>
<build>
<finalName>file-upload-demo</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
配置Spring MVC (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">
<display-name>File Upload Demo</display-name>
<!-- Spring MVC DispatcherServlet -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 编码过滤器 -->
<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>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
Spring MVC配置文件 (spring-mvc.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
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 开启注解扫描 -->
<context:component-scan base-package="com.example.controller"/>
<!-- 注解驱动 -->
<mvc:annotation-driven/>
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 配置文件上传解析器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设置上传文件的最大大小,单位字节 -->
<property name="maxUploadSize" value="10485760"/> <!-- 10MB -->
<!-- 设置上传文件的最大内存大小 -->
<property name="maxInMemorySize" value="4096"/>
<!-- 设置文件编码 -->
<property name="defaultEncoding" value="UTF-8"/>
</bean>
<!-- 静态资源处理 -->
<mvc:resources mapping="/resources/**" location="/resources/"/>
</beans>
文件上传Controller (FileUploadController.java)
package com.example.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
@Controller
@RequestMapping("/file")
public class FileUploadController {
// 上传文件保存路径
private static final String UPLOAD_DIR = "D:/uploads/";
/**
* 跳转到文件上传页面
*/
@GetMapping("/upload")
public String uploadPage() {
return "upload";
}
/**
* 单文件上传
*/
@PostMapping("/upload/single")
public String singleUpload(@RequestParam("file") MultipartFile file,
@RequestParam(value = "description", required = false) String description,
Model model) {
if (file.isEmpty()) {
model.addAttribute("message", "请选择要上传的文件!");
return "result";
}
try {
// 获取原始文件名
String originalFilename = file.getOriginalFilename();
// 获取文件扩展名
String extension = "";
if (originalFilename != null && originalFilename.contains(".")) {
extension = originalFilename.substring(originalFilename.lastIndexOf("."));
}
// 生成新的文件名(使用UUID避免文件名冲突)
String newFilename = UUID.randomUUID().toString().replace("-", "") + extension;
// 创建日期文件夹
String dateDir = new SimpleDateFormat("yyyyMMdd").format(new Date());
String uploadDirPath = UPLOAD_DIR + dateDir + "/";
File dir = new File(uploadDirPath);
if (!dir.exists()) {
dir.mkdirs();
}
// 保存文件
File destFile = new File(uploadDirPath + newFilename);
file.transferTo(destFile);
// 设置返回信息
model.addAttribute("message", "文件上传成功!");
model.addAttribute("originalFilename", originalFilename);
model.addAttribute("newFilename", newFilename);
model.addAttribute("fileSize", file.getSize() / 1024 + " KB");
model.addAttribute("filePath", dateDir + "/" + newFilename);
model.addAttribute("description", description);
} catch (IOException e) {
e.printStackTrace();
model.addAttribute("message", "文件上传失败:" + e.getMessage());
}
return "result";
}
/**
* 多文件上传(方式一:使用MultipartHttpServletRequest)
*/
@PostMapping("/upload/multiple1")
public String multipleUpload1(MultipartHttpServletRequest request, Model model) {
List<MultipartFile> files = request.getFiles("files");
List<String> uploadResults = new ArrayList<>();
if (files == null || files.isEmpty()) {
model.addAttribute("message", "请选择要上传的文件!");
return "result";
}
for (MultipartFile file : files) {
if (!file.isEmpty()) {
try {
String originalFilename = file.getOriginalFilename();
String extension = "";
if (originalFilename != null && originalFilename.contains(".")) {
extension = originalFilename.substring(originalFilename.lastIndexOf("."));
}
String newFilename = UUID.randomUUID().toString().replace("-", "") + extension;
String dateDir = new SimpleDateFormat("yyyyMMdd").format(new Date());
String uploadDirPath = UPLOAD_DIR + dateDir + "/";
File dir = new File(uploadDirPath);
if (!dir.exists()) {
dir.mkdirs();
}
File destFile = new File(uploadDirPath + newFilename);
file.transferTo(destFile);
uploadResults.add("文件 '" + originalFilename + "' 上传成功");
} catch (IOException e) {
e.printStackTrace();
uploadResults.add("文件 '" + file.getOriginalFilename() + "' 上传失败:" + e.getMessage());
}
}
}
model.addAttribute("message", "多文件上传完成!");
model.addAttribute("uploadResults", uploadResults);
return "result";
}
/**
* 多文件上传(方式二:使用数组参数)
*/
@PostMapping("/upload/multiple2")
public String multipleUpload2(@RequestParam("files") MultipartFile[] files, Model model) {
List<String> uploadResults = new ArrayList<>();
if (files == null || files.length == 0) {
model.addAttribute("message", "请选择要上传的文件!");
return "result";
}
for (MultipartFile file : files) {
if (!file.isEmpty()) {
try {
String originalFilename = file.getOriginalFilename();
String extension = "";
if (originalFilename != null && originalFilename.contains(".")) {
extension = originalFilename.substring(originalFilename.lastIndexOf("."));
}
String newFilename = UUID.randomUUID().toString().replace("-", "") + extension;
String dateDir = new SimpleDateFormat("yyyyMMdd").format(new Date());
String uploadDirPath = UPLOAD_DIR + dateDir + "/";
File dir = new File(uploadDirPath);
if (!dir.exists()) {
dir.mkdirs();
}
File destFile = new File(uploadDirPath + newFilename);
file.transferTo(destFile);
uploadResults.add("文件 '" + originalFilename + "' 上传成功,新文件名:" + newFilename);
} catch (IOException e) {
e.printStackTrace();
uploadResults.add("文件 '" + file.getOriginalFilename() + "' 上传失败:" + e.getMessage());
}
}
}
model.addAttribute("message", "多文件上传完成!");
model.addAttribute("uploadResults", uploadResults);
return "result";
}
/**
* AJAX异步文件上传
*/
@PostMapping("/upload/ajax")
@ResponseBody
public String ajaxUpload(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return "{\"success\": false, \"message\": \"文件为空\"}";
}
try {
String originalFilename = file.getOriginalFilename();
String extension = "";
if (originalFilename != null && originalFilename.contains(".")) {
extension = originalFilename.substring(originalFilename.lastIndexOf("."));
}
String newFilename = UUID.randomUUID().toString().replace("-", "") + extension;
String dateDir = new SimpleDateFormat("yyyyMMdd").format(new Date());
String uploadDirPath = UPLOAD_DIR + dateDir + "/";
File dir = new File(uploadDirPath);
if (!dir.exists()) {
dir.mkdirs();
}
File destFile = new File(uploadDirPath + newFilename);
file.transferTo(destFile);
return "{\"success\": true, \"message\": \"文件上传成功\", \"filename\": \"" + newFilename + "\"}";
} catch (IOException e) {
e.printStackTrace();
return "{\"success\": false, \"message\": \"文件上传失败: " + e.getMessage() + "\"}";
}
}
/**
* 上传进度获取(简化版)
*/
@GetMapping("/upload/progress")
@ResponseBody
public String getUploadProgress(HttpServletRequest request) {
// 这里需要配合实现ProgressListener来获取真实进度
// 简化示例,返回模拟数据
return "{\"progress\": 50}";
}
}
文件上传页面 (upload.jsp)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">文件上传</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
}
.upload-section {
background: #f5f5f5;
padding: 20px;
margin-bottom: 20px;
border-radius: 5px;
}
.upload-section h3 {
margin-top: 0;
color: #333;
}
.upload-form {
margin: 15px 0;
}
.btn-upload {
background: #007bff;
color: white;
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
margin-top: 10px;
}
.btn-upload:hover {
background: #0056b3;
}
.description-input {
display: block;
margin: 10px 0;
padding: 5px;
width: 300px;
}
.progress-bar {
margin-top: 10px;
display: none;
}
.progress {
width: 100%;
background: #e0e0e0;
padding: 3px;
}
.progress-inner {
background: #76b852;
height: 20px;
width: 0;
text-align: center;
white-space: nowrap;
}
</style>
</head>
<body>
<h1>文件上传示例</h1>
<!-- 单文件上传 -->
<div class="upload-section">
<h3>单文件上传</h3>
<form action="${pageContext.request.contextPath}/file/upload/single"
method="post" enctype="multipart/form-data" class="upload-form">
<input type="text" name="description" placeholder="文件描述(可选)" class="description-input"/>
<br/>
<input type="file" name="file" required/>
<br/>
<button type="submit" class="btn-upload">上传单个文件</button>
</form>
</div>
<!-- 多文件上传 - 方式一 -->
<div class="upload-section">
<h3>多文件上传(方法一)</h3>
<form action="${pageContext.request.contextPath}/file/upload/multiple1"
method="post" enctype="multipart/form-data" class="upload-form">
<input type="file" name="files" multiple required/>
<br/>
<button type="submit" class="btn-upload">上传多个文件</button>
</form>
</div>
<!-- 多文件上传 - 方式二 -->
<div class="upload-section">
<h3>多文件上传(方法二 - 使用数组)</h3>
<form action="${pageContext.request.contextPath}/file/upload/multiple2"
method="post" enctype="multipart/form-data" class="upload-form">
<input type="file" name="files" multiple required/>
<br/>
<button type="submit" class="btn-upload">上传多个文件</button>
</form>
</div>
<!-- AJAX上传 -->
<div class="upload-section">
<h3>AJAX异步上传</h3>
<input type="file" id="fileInput" name="file"/>
<div class="progress-bar">
<div class="progress">
<div class="progress-inner" id="progressBar">0%</div>
</div>
</div>
<button class="btn-upload" onclick="ajaxUpload()">AJAX上传文件</button>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
function ajaxUpload() {
var fileInput = document.getElementById('fileInput');
if (!fileInput.files || !fileInput.files[0]) {
alert('请选择文件');
return;
}
var formData = new FormData();
formData.append('file', fileInput.files[0]);
// 显示进度条
$('.progress-bar').show();
$.ajax({
url: '${pageContext.request.contextPath}/file/upload/ajax',
type: 'POST',
data: formData,
contentType: false,
processData: false,
xhr: function() {
var xhr = $.ajaxSettings.xhr();
if (xhr.upload) {
xhr.upload.progress = function(e) {
if (e.lengthComputable) {
var percent = Math.round((e.loaded / e.total) * 100);
$('#progressBar').css('width', percent + '%').text(percent + '%');
}
};
}
return xhr;
},
success: function(response) {
var result = JSON.parse(response);
if (result.success) {
alert('上传成功!文件名:' + result.filename);
} else {
alert('上传失败:' + result.message);
}
setTimeout(function() {
$('.progress-bar').hide();
$('#progressBar').css('width', '0').text('0%');
}, 2000);
},
error: function() {
alert('请求失败');
$('.progress-bar').hide();
$('#progressBar').css('width', '0').text('0%');
}
});
}
</script>
</body>
</html>
上传结果页面 (result.jsp)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">上传结果</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
}
.result-box {
background: #f9f9f9;
border: 1px solid #ddd;
padding: 20px;
border-radius: 5px;
}
.success {
color: #28a745;
}
.error {
color: #dc3545;
}
.info-item {
margin: 10px 0;
padding: 10px;
background: #fff;
border-radius: 3px;
border-left: 4px solid #007bff;
}
.info-label {
font-weight: bold;
color: #333;
}
.info-list {
list-style: none;
padding: 0;
}
.info-list li {
margin: 5px 0;
padding: 5px;
background: #fff;
border-radius: 3px;
}
</style>
</head>
<body>
<div class="result-box">
<h2 class="${message.startsWith('上传成功') ? 'success' : 'error'}">${message}</h2>
<c:if test="${not empty originalFilename}">
<div class="info-item">
<span class="info-label">原始文件名:</span>${originalFilename}
</div>
</c:if>
<c:if test="${not empty newFilename}">
<div class="info-item">
<span class="info-label">新文件名:</span>${newFilename}
</div>
</c:if>
<c:if test="${not empty fileSize}">
<div class="info-item">
<span class="info-label">文件大小:</span>${fileSize}
</div>
</c:if>
<c:if test="${not empty filePath}">
<div class="info-item">
<span class="info-label">保存路径:</span>${filePath}
</div>
</c:if>
<c:if test="${not empty description}">
<div class="info-item">
<span class="info-label">文件描述:</span>${description}
</div>
</c:if>
<c:if test="${not empty uploadResults}">
<div class="info-item">
<span class="info-label">多文件上传结果:</span>
<ul class="info-list">
<c:forEach items="${uploadResults}" var="result">
<li>${result}</li>
</c:forEach>
</ul>
</div>
</c:if>
<p><a href="${pageContext.request.contextPath}/file/upload">返回上传页面</a></p>
</div>
</body>
</html>
项目结构
file-upload-demo/
├── pom.xml
└── src
└── main
├── java
│ └── com/example/controller/
│ └── FileUploadController.java
├── resources
│ └── spring-mvc.xml
└── webapp
├── WEB-INF
│ ├── views
│ │ ├── result.jsp
│ │ └── upload.jsp
│ └── web.xml
└── index.jsp
配置文件上传大小限制
如果需要修改上传文件大小限制,可以在spring-mvc.xml中调整maxUploadSize属性,或者在项目的配置文件中配置:
# application.properties 中的配置 spring.servlet.multipart.max-file-size=10MB spring.servlet.multipart.max-request-size=10MB
注意事项
- 目录权限:确保
UPLOAD_DIR目录存在且有写入权限 - 文件类型校验:生产环境建议添加文件类型校验,防止上传不安全文件
- 文件大小限制:根据实际需求调整上传大小限制
- 路径安全:使用UUID生成文件名,避免路径穿越攻击
- 错误处理:添加全局异常处理器,提供更好的错误提示
- 进度监控:对于大文件上传,可以添加进度监控功能
这个案例包含了常见的文件上传场景:单文件上传、多文件上传和AJAX异步上传,您可以根据实际需求进行修改和扩展。