杰瑞科技汇

HTML Java如何实现文件上传?

  1. 前端 (HTML):创建一个用户可以选择和提交文件的表单。
  2. 后端 (Java):接收前端传来的文件数据,并将其保存到服务器的指定目录中。

第一步:前端实现 (HTML)

前端的核心是使用一个 <form> 标签,并设置几个关键属性。

HTML Java如何实现文件上传?-图1
(图片来源网络,侵删)

关键点:

  • method="post":文件上传必须使用 POST 方法,因为 GET 方法有 URL 长度限制,且不安全。
  • enctype="multipart/form-data"这是最关键的一步,这个属性告诉浏览器,表单中包含文件等二进制数据,需要使用特殊的 MIME 类型进行编码,没有它,后端将无法正确接收文件。
  • <input type="file">:标准的文件选择输入控件。

示例代码 (upload.html)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">文件上传示例</title>
    <style>
        body { font-family: sans-serif; padding: 20px; }
        .container { max-width: 600px; margin: auto; border: 1px solid #ccc; padding: 20px; border-radius: 8px; }
        h2 { text-align: center; }
        input[type="file"] { width: 100%; padding: 8px; margin-bottom: 10px; }
        input[type="submit"] { background-color: #007bff; color: white; padding: 10px 15px; border: none; border-radius: 4px; cursor: pointer; width: 100%; }
        input[type="submit"]:hover { background-color: #0056b3; }
    </style>
</head>
<body>
    <div class="container">
        <h2>上传文件</h2>
        <form action="upload" method="post" enctype="multipart/form-data">
            <!-- 
              action="upload": 指定表单提交到的后端URL。
              在Servlet中,这通常对应一个@WebServlet("/upload")的类。
              在Spring Boot中,这通常对应一个 @PostMapping("/upload") 的方法。
            -->
            <label for="file">请选择文件:</label>
            <input type="file" id="file" name="file" required> <!-- name="file" 是后端用来识别这个文件的key -->
            <input type="submit" value="上传文件">
        </form>
    </div>
</body>
</html>

说明:

  • action="upload":这个值取决于你使用的后端技术,我们假设后端有一个处理 /upload 路径的接口。
  • name="file":这个 name 属性非常重要,后端代码会通过这个名称来获取上传的文件对象,你可以把它想象成一个“键”,前端把文件数据作为“值”发送过去。

第二步:后端实现 (Java)

后端负责解析 multipart/form-data 格式的请求数据,并提取出文件,我们将介绍两种主流的 Java 后端实现方式:

  1. 传统 Servlet (Java EE / Jakarta EE)
  2. 现代 Spring Boot

使用传统 Servlet

你需要一个支持 Servlet 3.0+ 的服务器(如 Tomcat 9, Jetty 9)。

HTML Java如何实现文件上传?-图2
(图片来源网络,侵删)

创建 Maven 项目并添加依赖

pom.xml 中添加 javax.servlet-api 依赖。

<dependencies>
    <!-- Jakarta Servlet API (对于 Tomcat 10+) -->
    <dependency>
        <groupId>jakarta.servlet</groupId>
        <artifactId>jakarta.servlet-api</artifactId>
        <version>6.0.0</version>
        <scope>provided</scope> <!-- 服务器会提供,所以打包时不需要 -->
    </dependency>
</dependencies>

创建上传目录

在你的项目根目录下(src/main/webapp 旁边),创建一个目录来存放上传的文件,uploads

HTML Java如何实现文件上传?-图3
(图片来源网络,侵删)

编写 Servlet 类

创建一个 Java 类来处理文件上传请求。

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.MultipartConfig; // 声明这是一个支持文件上传的Servlet
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.Part;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
@WebServlet("/upload") // 对应HTML中form的action属性
@MultipartConfig // 必须添加此注解,Servlet才能处理Part对象
public class FileUploadServlet extends HttpServlet {
    // 定义上传文件的存储目录
    private static final String UPLOAD_DIR = "uploads";
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        // 1. 获取上传目录的绝对路径
        // getServletContext().getRealPath() 获取 webapp 目录的路径
        String applicationPath = getServletContext().getRealPath("");
        String uploadFilePath = applicationPath + File.separator + UPLOAD_DIR;
        // 2. 如果目录不存在,则创建
        File uploadDir = new File(uploadFilePath);
        if (!uploadDir.exists()) {
            uploadDir.mkdirs();
        }
        // 3. 从请求中获取 Part 对象(对应前端的 <input type="file">)
        // name="file" 必须和HTML中的input的name属性一致
        Part filePart = request.getPart("file");
        if (filePart == null) {
            response.getWriter().println("错误:没有选择文件!");
            return;
        }
        // 4. 获取文件名
        String fileName = filePart.getSubmittedFileName();
        if (fileName == null || fileName.isEmpty()) {
            response.getWriter().println("错误:文件名为空!");
            return;
        }
        // 5. 构建目标文件的完整路径
        Path targetPath = Paths.get(uploadFilePath, fileName);
        // 6. 将上传的文件内容写入到服务器硬盘
        // 使用 try-with-resources 确保 InputStream 被正确关闭
        try (InputStream inputStream = filePart.getInputStream()) {
            Files.copy(inputStream, targetPath, StandardCopyOption.REPLACE_EXISTING);
        }
        // 7. 返回成功信息
        response.getWriter().println("文件 " + fileName + " 上传成功!保存在: " + targetPath);
    }
}

代码解释:

  • @WebServlet("/upload"):将这个 Servlet 映射到 /upload URL。
  • @MultipartConfig必需,这个注解告诉容器,这个 Servlet 可以处理 multipart/form-data 请求,并提供了 getPart() 方法。
  • request.getPart("file"):根据前端 inputname 属性获取 Part 对象,Part 封装了上传文件的所有信息(内容、文件名、大小等)。
  • filePart.getInputStream():获取文件的输入流,我们可以将其写入到服务器的文件系统中。
  • Files.copy(...):一个非常方便的 NIO 工具,用于将输入流的内容复制到目标文件。

使用 Spring Boot (更推荐)

Spring Boot 简化了配置,处理文件上传非常方便。

创建 Spring Boot 项目

使用 Spring Initializr 创建一个新项目,添加以下依赖:

  • Spring Web: 用于构建 Web 应用。
  • Spring Boot DevTools: (可选) 用于热部署。

配置上传目录

src/main/resources 目录下创建 application.propertiesapplication.yml 文件。

application.properties:

# 设置单个文件的最大大小 (5MB)
spring.servlet.multipart.max-file-size=5MB
# 设置总请求的最大大小 (包含多个文件,10MB)
spring.servlet.multipart.max-request-size=10MB

编写 Controller

创建一个 Controller 来处理文件上传请求。

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
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.util.UUID;
@Controller
public class FileUploadController {
    // 定义上传文件的存储目录,放在项目根目录下
    private static final String UPLOADED_FOLDER = "uploads/";
    @PostMapping("/upload") // 对应HTML中form的action属性
    public String handleFileUpload(@RequestParam("file") MultipartFile file,
                                   RedirectAttributes redirectAttributes) {
        if (file.isEmpty()) {
            redirectAttributes.addFlashAttribute("message", "请选择一个文件上传");
            return "redirect:uploadStatus"; // 重定向到上传状态页面
        }
        try {
            // 确保上传目录存在
            Path uploadPath = Paths.get(UPLOADED_FOLDER);
            if (!Files.exists(uploadPath)) {
                Files.createDirectories(uploadPath);
            }
            // 为了防止文件名冲突,可以生成一个唯一的文件名
            String originalFilename = file.getOriginalFilename();
            String uniqueFilename = UUID.randomUUID().toString() + "_" + originalFilename;
            // 将文件保存到目标路径
            Path targetPath = uploadPath.resolve(uniqueFilename);
            Files.copy(file.getInputStream(), targetPath, StandardCopyOption.REPLACE_EXISTING);
            redirectAttributes.addFlashAttribute("message",
                    "文件 '" + uniqueFilename + "' 上传成功!");
        } catch (IOException e) {
            e.printStackTrace();
            redirectAttributes.addFlashAttribute("message", "上传失败: " + e.getMessage());
        }
        return "redirect:/uploadStatus"; // 重定向到上传状态页面
    }
    // 一个简单的页面,用于显示上传结果
    @GetMapping("/uploadStatus")
    public String uploadStatus() {
        return "uploadStatus"; // 返回一个 Thymeleaf 或其他模板引擎的HTML页面
    }
}

创建上传状态页面 (src/main/resources/templates/uploadStatus.html)

如果你使用 Thymeleaf,可以创建一个页面来显示消息。

<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">上传状态</title>
</head>
<body>
    <h1>上传结果</h1>
    <p th:if="${message}" th:text="${message}"></p>
    <a href="/">返回上传页面</a>
</body>
</html>

Spring Boot 代码解释:

  • @Controller:声明这是一个 Spring MVC 的控制器。
  • @PostMapping("/upload"):处理 POST 请求到 /upload 路径。
  • @RequestParam("file") MultipartFile file:Spring Boot 会自动将请求中的文件数据绑定到 MultipartFile 对象。MultipartFile 是 Spring 封装的上传文件接口,比 Servlet 的 Part 更易用。
  • file.getOriginalFilename():获取原始文件名。
  • file.getInputStream():获取文件的输入流。
  • redirectAttributes.addFlashAttribute():用于在重定向时传递消息,消息只存在于下一次请求中。
  • return "redirect:/uploadStatus":使用重定向可以防止用户刷新页面导致文件重复上传。

总结与对比

特性 传统 Servlet Spring Boot
依赖 需要 servlet-api,由服务器提供 spring-boot-starter-web
配置 需要手动创建目录,代码中处理 application.properties 中简单配置文件大小限制
核心类 @Part MultipartFile
代码量 较多,需要手动处理路径、创建目录等 较少,Spring 自动处理很多底层细节
推荐度 适合学习 Servlet 原理或在不使用框架的旧项目中 强烈推荐,开发效率高,代码简洁,功能强大

对于现代 Web 开发,Spring Boot 是首选方案,它极大地简化了文件上传及其他许多常见任务,而传统的 Servlet 方式则有助于理解 Web 请求和响应的底层机制。

分享:
扫描分享到社交APP
上一篇
下一篇