怎么实现Java文件上传案例

wen java案例 3

本文目录导读:

怎么实现Java文件上传案例

  1. 项目结构
  2. Maven依赖 (pom.xml)
  3. 配置文件 (application.yml)
  4. 主应用类
  5. 文件上传配置类
  6. 文件存储服务
  7. 文件上传控制器
  8. 前端页面 (static/upload.html)
  9. 运行测试
  10. 高级功能
  11. 注意事项

我来为您提供一个完整的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 测试方法

  1. 浏览器访问:打开 http://localhost:8080
  2. 单个文件上传:选择一个文件,点击"上传文件"
  3. 多个文件上传:选择多个文件(Ctrl+Click 或 Cmd+Click),点击"上传多个文件"
  4. 下载测试:上传成功后,点击"下载文件"链接

高级功能

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);

注意事项

  1. 安全性:建议添加文件类型校验和病毒扫描
  2. 性能:大文件上传建议使用分片上传
  3. 存储:生产环境建议使用云存储(OSS/S3)
  4. 限制:合理设置文件大小限制,防止恶意攻击
  5. 日志:记录文件上传日志,便于追踪

这个案例提供了完整的Java文件上传功能,包含前后端实现、文件校验、多文件上传等特性,您可以根据实际需求进行调整和扩展。

抱歉,评论功能暂时关闭!