本文目录导读:

- 项目结构
- Maven依赖 (pom.xml)
- 配置文件 (application.yml)
- 主应用类
- 文件上传配置类
- 文件存储服务
- 文件上传控制器
- 前端页面 (static/upload.html)
- 运行测试
- 高级功能
- 注意事项
我来为您提供一个完整的Java文件上传案例,包含前端和后端的实现。
项目结构
file-upload-demo/
├── pom.xml
├── src/
│ └── main/
│ ├── java/
│ │ └── com/example/
│ │ ├── FileUploadApplication.java
│ │ ├── controller/
│ │ │ └── FileUploadController.java
│ │ ├── service/
│ │ │ └── FileStorageService.java
│ │ └── config/
│ │ └── FileUploadConfig.java
│ └── resources/
│ ├── application.yml
│ └── static/
│ └── index.html
Maven依赖 (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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>file-upload-demo</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- Web starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Thymeleaf 模板引擎 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- 文件上传处理 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<!-- Lombok 简化代码 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 测试依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
配置文件 (application.yml)
server:
port: 8080
spring:
servlet:
multipart:
# 单个文件最大大小
max-file-size: 10MB
# 请求中所有文件总大小
max-request-size: 50MB
# 文件上传阈值
file-size-threshold: 2KB
# 上传文件存储位置
location: ./uploads/
# 自定义文件上传配置
file:
upload:
# 上传目录
path: ./uploads/
# 允许的文件类型
allowed-types: image/jpeg,image/png,image/gif,application/pdf,application/msword
# 最大文件大小(字节)
max-size: 10485760
主应用类
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class FileUploadApplication {
public static void main(String[] args) {
SpringApplication.run(FileUploadApplication.class, args);
}
}
文件上传配置类
package com.example.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import lombok.Data;
@Data
@Configuration
@ConfigurationProperties(prefix = "file.upload")
public class FileUploadConfig {
// 上传路径
private String path;
// 允许的文件类型
private String[] allowedTypes;
// 最大文件大小
private long maxSize;
// 是否创建目录
private boolean createDirectory = true;
}
文件存储服务
package com.example.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import com.example.config.FileUploadConfig;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
@Service
public class FileStorageService {
@Autowired
private FileUploadConfig fileUploadConfig;
private Path fileStoragePath;
/**
* 存储单个文件
*/
public String storeFile(MultipartFile file) throws IOException {
// 检查文件是否为空
if (file.isEmpty()) {
throw new IOException("文件不能为空");
}
// 检查文件类型
if (!isAllowedFileType(file.getContentType())) {
throw new IOException("不支持的文件类型: " + file.getContentType());
}
// 检查文件大小
if (file.getSize() > fileUploadConfig.getMaxSize()) {
throw new IOException("文件大小超出限制");
}
// 创建存储目录
initStoragePath();
// 生成唯一的文件名
String originalFilename = StringUtils.cleanPath(file.getOriginalFilename());
String fileExtension = getFileExtension(originalFilename);
String uniqueFilename = UUID.randomUUID().toString() + fileExtension;
// 构建完整的存储路径
Path targetLocation = this.fileStoragePath.resolve(uniqueFilename);
// 复制文件到目标位置
Files.copy(file.getInputStream(), targetLocation, StandardCopyOption.REPLACE_EXISTING);
return uniqueFilename;
}
/**
* 存储多个文件
*/
public List<String> storeFiles(MultipartFile[] files) throws IOException {
java.util.List<String> storedFiles = new java.util.ArrayList<>();
for (MultipartFile file : files) {
if (!file.isEmpty()) {
String storedFilename = storeFile(file);
storedFiles.add(storedFilename);
}
}
return storedFiles;
}
/**
* 删除文件
*/
public boolean deleteFile(String filename) throws IOException {
if (filename == null || filename.isEmpty()) {
return false;
}
Path filePath = this.fileStoragePath.resolve(filename);
return Files.deleteIfExists(filePath);
}
/**
* 获取文件
*/
public Path getFile(String filename) {
return this.fileStoragePath.resolve(filename);
}
/**
* 检查文件类型是否允许
*/
private boolean isAllowedFileType(String contentType) {
if (contentType == null) {
return false;
}
return Arrays.asList(fileUploadConfig.getAllowedTypes()).contains(contentType);
}
/**
* 获取文件扩展名
*/
private String getFileExtension(String filename) {
int lastDotIndex = filename.lastIndexOf(".");
if (lastDotIndex == -1) {
return "";
}
return filename.substring(lastDotIndex);
}
/**
* 初始化存储路径
*/
private void initStoragePath() throws IOException {
if (fileStoragePath == null) {
fileStoragePath = Paths.get(fileUploadConfig.getPath())
.toAbsolutePath()
.normalize();
// 创建目录
if (fileUploadConfig.isCreateDirectory()) {
Files.createDirectories(fileStoragePath);
}
}
}
}
文件上传控制器
package com.example.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.http.HttpHeaders;
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.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.example.service.FileStorageService;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Path;
import java.util.List;
@Controller
public class FileUploadController {
@Autowired
private FileStorageService fileStorageService;
/**
* 显示上传页面
*/
@GetMapping("/")
public String index() {
return "upload";
}
/**
* 处理单个文件上传
*/
@PostMapping("/upload")
public String uploadFile(@RequestParam("file") MultipartFile file,
RedirectAttributes redirectAttributes) {
try {
String storedFilename = fileStorageService.storeFile(file);
redirectAttributes.addFlashAttribute("message",
"文件上传成功: " + file.getOriginalFilename());
redirectAttributes.addFlashAttribute("filename", storedFilename);
} catch (IOException e) {
redirectAttributes.addFlashAttribute("error",
"文件上传失败: " + e.getMessage());
}
return "redirect:/";
}
/**
* 处理多个文件上传
*/
@PostMapping("/uploadMultiple")
public String uploadMultipleFiles(@RequestParam("files") MultipartFile[] files,
RedirectAttributes redirectAttributes) {
try {
List<String> storedFilenames = fileStorageService.storeFiles(files);
redirectAttributes.addFlashAttribute("message",
"成功上传 " + storedFilenames.size() + " 个文件");
} catch (IOException e) {
redirectAttributes.addFlashAttribute("error",
"文件上传失败: " + e.getMessage());
}
return "redirect:/";
}
/**
* 下载文件
*/
@GetMapping("/download/{filename:.+}")
public ResponseEntity<Resource> downloadFile(@PathVariable String filename) {
try {
Path filePath = fileStorageService.getFile(filename);
Resource resource = new UrlResource(filePath.toUri());
if (resource.exists()) {
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + resource.getFilename() + "\"")
.body(resource);
} else {
return ResponseEntity.notFound().build();
}
} catch (MalformedURLException e) {
return ResponseEntity.badRequest().build();
}
}
/**
* 删除文件
*/
@PostMapping("/delete/{filename}")
public String deleteFile(@PathVariable String filename,
RedirectAttributes redirectAttributes) {
try {
fileStorageService.deleteFile(filename);
redirectAttributes.addFlashAttribute("message", "文件删除成功: " + filename);
} catch (IOException e) {
redirectAttributes.addFlashAttribute("error", "文件删除失败: " + e.getMessage());
}
return "redirect:/";
}
}
前端页面 (static/upload.html)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">Java文件上传案例</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 40px 20px;
}
.container {
max-width: 800px;
margin: 0 auto;
background: white;
border-radius: 15px;
padding: 40px;
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.1);
}
h1 {
color: #333;
margin-bottom: 30px;
text-align: center;
font-size: 28px;
}
h2 {
color: #555;
margin-bottom: 20px;
font-size: 20px;
}
.upload-section {
background: #f5f5f5;
border: 2px dashed #ccc;
border-radius: 10px;
padding: 30px;
margin-bottom: 20px;
transition: border-color 0.3s;
}
.upload-section:hover {
border-color: #667eea;
}
.file-input {
margin: 20px 0;
width: 100%;
padding: 15px;
border: 1px solid #ddd;
border-radius: 5px;
background: white;
}
.upload-btn {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
padding: 12px 30px;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
transition: transform 0.2s;
}
.upload-btn:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
}
.message {
margin: 20px 0;
padding: 15px;
border-radius: 5px;
font-size: 14px;
line-height: 1.6;
}
.success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.divider {
border: none;
border-top: 2px solid #eee;
margin: 30px 0;
}
.file-info {
background: #f8f9fa;
padding: 10px;
border-radius: 5px;
margin: 10px 0;
font-size: 14px;
}
</style>
</head>
<body>
<div class="container">
<h1>📁 Java 文件上传案例</h1>
<!-- 消息提示 -->
<div th:if="${message}" class="message success" th:text="${message}"></div>
<div th:if="${error}" class="message error" th:text="${error}"></div>
<!-- 单个文件上传 -->
<h2>单个文件上传</h2>
<div class="upload-section">
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="file" class="file-input" required>
<button type="submit" class="upload-btn">📤 上传文件</button>
</form>
</div>
<hr class="divider">
<!-- 多个文件上传 -->
<h2>多个文件上传</h2>
<div class="upload-section">
<form action="/uploadMultiple" method="post" enctype="multipart/form-data">
<input type="file" name="files" class="file-input" multiple required>
<button type="submit" class="upload-btn">📤 上传多个文件</button>
</form>
</div>
<hr class="divider">
<!-- 已上传文件信息 -->
<div th:if="${filename}" class="file-info">
<h3 style="margin-bottom: 15px;">📄 已上传文件:</h3>
<p><strong>文件名:</strong><span th:text="${filename}"></span></p>
<a th:href="@{'/download/' + ${filename}}"
class="upload-btn"
style="display: inline-block; text-decoration: none; margin-top: 10px;">
⬇️ 下载文件
</a>
</div>
<!-- 上传提示信息 -->
<div style="margin-top: 30px; padding: 20px; background: #e3f2fd; border-radius: 5px;">
<h3 style="margin-bottom: 10px;">📋 上传规范</h3>
<ul style="margin-left: 20px; line-height: 1.8;">
<li>允许的文件类型:图片(JPG/PNG/GIF)、PDF、Word 文档</li>
<li>单个文件最大大小:10MB</li>
<li>多个文件总大小:50MB</li>
</ul>
</div>
</div>
</body>
</html>
运行测试
1 启动应用
mvn spring-boot:run
2 测试方法
- 浏览器访问:打开
http://localhost:8080 - 单个文件上传:选择一个文件,点击"上传文件"
- 多个文件上传:选择多个文件(Ctrl+Click 或 Cmd+Click),点击"上传多个文件"
- 下载测试:上传成功后,点击"下载文件"链接
高级功能
1 增加文件预览
@GetMapping("/preview/{filename:.+}")
public ResponseEntity<Resource> previewFile(@PathVariable String filename) {
try {
Path filePath = fileStorageService.getFile(filename);
Resource resource = new UrlResource(filePath.toUri());
if (resource.exists()) {
// 根据文件扩展名返回不同的 Content-Type
String contentType = determineContentType(filename);
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType(contentType))
.body(resource);
} else {
return ResponseEntity.notFound().build();
}
} catch (Exception e) {
return ResponseEntity.badRequest().build();
}
}
private String determineContentType(String filename) {
if (filename.endsWith(".pdf")) return "application/pdf";
if (filename.endsWith(".jpg") || filename.endsWith(".jpeg")) return "image/jpeg";
if (filename.endsWith(".png")) return "image/png";
return "application/octet-stream";
}
2 增加进度条
// 前端使用 XMLHttpRequest 实现上传进度
const xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', (event) => {
if (event.lengthComputable) {
const percentCompleted = (event.loaded / event.total) * 100;
// 更新进度条
progressBar.style.width = percentCompleted + '%';
}
});
xhr.open('POST', '/upload');
const formData = new FormData();
formData.append('file', fileInput.files[0]);
xhr.send(formData);
注意事项
- 安全性:建议添加文件类型校验和病毒扫描
- 性能:大文件上传建议使用分片上传
- 存储:生产环境建议使用云存储(OSS/S3)
- 限制:合理设置文件大小限制,防止恶意攻击
- 日志:记录文件上传日志,便于追踪
这个案例提供了完整的Java文件上传功能,包含前后端实现、文件校验、多文件上传等特性,您可以根据实际需求进行调整和扩展。