杰瑞科技汇

Java如何接收FormData数据?

下面我将从最简单的方式(使用 Spring Boot)到更底层的 Servlet API,为你详细讲解如何在 Java 中接收 multipart/form-data

Java如何接收FormData数据?-图1
(图片来源网络,侵删)

场景分析

一个 multipart/form-data 请求通常包含两部分或更多部分:

  1. 普通表单字段username, description 等,是文本数据。
  2. 文件字段avatar, document 等,是二进制文件数据。

我们的目标就是高效、安全地解析这些数据。


使用 Spring Boot (推荐)

Spring Boot 对 multipart/form-data 提供了极其强大且简洁的支持,这是目前最主流、最推荐的方式。

添加依赖

确保你的 pom.xml 文件中包含了 spring-boot-starter-web 依赖,它已经包含了处理 multipart 请求所需的所有依赖。

Java如何接收FormData数据?-图2
(图片来源网络,侵删)
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

配置上传文件大小(可选但推荐)

为了避免上传大文件时出现问题,建议在 application.propertiesapplication.yml 中进行配置。

application.properties

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

创建 Controller 接收数据

在 Controller 中,你可以使用 @RequestParam 注解来接收普通字段和文件。

示例 1:接收普通文本字段

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class FormController {
    @PostMapping("/api/submit-text")
    public String submitText(
            @RequestParam("username") String username,
            @RequestParam("description") String description) {
        System.out.println("Username: " + username);
        System.out.println("Description: " + description);
        return "Text received successfully: " + username;
    }
}

使用 curl 测试:

Java如何接收FormData数据?-图3
(图片来源网络,侵删)
curl -X POST http://localhost:8080/api/submit-text \
  -H "Content-Type: multipart/form-data" \
  -F "username=zhangsan" \
  -F "description=This is a test description."

示例 2:接收单个文件

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
@RestController
public class FileUploadController {
    @PostMapping("/api/upload")
    public String uploadFile(@RequestParam("file") MultipartFile file) {
        if (file.isEmpty()) {
            return "Please select a file to upload.";
        }
        try {
            // 获取文件原始名称
            String originalFilename = file.getOriginalFilename();
            // 定义文件存储路径
            String uploadDir = "uploads/";
            // 创建上传目录(如果不存在)
            File uploadPath = new File(uploadDir);
            if (!uploadPath.exists()) {
                uploadPath.mkdirs();
            }
            // 保存文件到服务器
            String filePath = uploadDir + originalFilename;
            file.transferTo(new File(filePath));
            return "File uploaded successfully: " + filePath;
        } catch (IOException e) {
            e.printStackTrace();
            return "Failed to upload file: " + e.getMessage();
        }
    }
}

使用 curl 测试: 假设你有一个名为 test.txt 的文件。

curl -X POST http://localhost:8080/api/upload \
  -H "Content-Type: multipart/form-data" \
  -F "file=@test.txt"

示例 3:同时接收文本字段和多个文件

这是最常见的情况,Spring 会自动将 MultipartFile[] 绑定到同名参数上。

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.List;
@RestController
public class MixedDataController {
    @PostMapping("/api/submit-mixed")
    public String submitMixed(
            @RequestParam("title") String title,
            @RequestParam("files") MultipartFile[] files) {
        System.out.println("Title: " + title);
        System.out.println("Number of files received: " + files.length);
        for (MultipartFile file : files) {
            if (!file.isEmpty()) {
                try {
                    String originalFilename = file.getOriginalFilename();
                    String uploadDir = "uploads/";
                    File uploadPath = new File(uploadDir);
                    if (!uploadPath.exists()) {
                        uploadPath.mkdirs();
                    }
                    String filePath = uploadDir + originalFilename;
                    file.transferTo(new File(filePath));
                    System.out.println("Saved file: " + filePath);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return "Mixed data received successfully!";
    }
}

使用 curl 测试:

curl -X POST http://localhost:8080/api/submit-mixed \
  -H "Content-Type: multipart/form-data" \
  -F "title=My Project" \
  -F "files=@file1.jpg" \
  -F "files=@file2.png"

使用原生 Servlet API

如果你不使用 Spring Boot,或者需要更底层地控制解析过程,可以使用 Servlet API,这需要你手动处理解析逻辑。

创建 Servlet

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
@MultipartConfig(
    fileSizeThreshold = 1024 * 1024 * 1, // 1 MB
    maxFileSize = 1024 * 1024 * 10,      // 10 MB
    maxRequestSize = 1024 * 1024 * 15    // 15 MB
)
public class FormDataServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        // 1. 获取普通文本字段
        String username = request.getParameter("username");
        System.out.println("Username: " + username);
        // 2. 获取文件部分
        Part filePart = request.getPart("file");
        if (filePart != null && filePart.getSize() > 0) {
            String fileName = getSubmittedFileName(filePart); // 获取原始文件名
            String uploadPath = getServletContext().getReal("") + File.separator + "uploads";
            // 创建上传目录
            File uploadDir = new File(uploadPath);
            if (!uploadDir.exists()) {
                uploadDir.mkdirs();
            }
            // 写入文件
            filePart.write(uploadPath + File.separator + fileName);
            System.out.println("File uploaded to: " + uploadPath + File.separator + fileName);
        }
        // 3. 响应客户端
        response.setContentType("text/plain");
        PrintWriter out = response.getWriter();
        out.println("Data received successfully!");
    }
    // Helper method to get the submitted file name
    private String getSubmittedFileName(Part part) {
        for (String cd : part.getHeader("content-disposition").split(";")) {
            if (cd.trim().startsWith("filename")) {
                String fileName = cd.substring(cd.indexOf('=') + 1).trim().replace("\"", "");
                return fileName.substring(fileName.lastIndexOf('/') + 1).substring(fileName.lastIndexOf('\\') + 1);
            }
        }
        return null;
    }
}

配置 web.xml 或使用注解

使用注解 (推荐): 在 Servlet 类上添加 @WebServlet("/api/servlet-upload") 即可。

使用 web.xml:

<servlet>
    <servlet-name>formDataServlet</servlet-name>
    <servlet-class>com.example.FormDataServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>formDataServlet</servlet-name>
    <url-pattern>/api/servlet-upload</url-pattern>
</servlet-mapping>

@MultipartConfig 注解

这个注解是关键,它告诉 Servlet 容器这个 Servlet 能够处理 multipart/form-data 请求,它包含以下重要属性:

  • fileSizeThreshold: 当文件大小超过这个阈值时,内容会被临时存储到磁盘上,否则存在内存中。
  • maxFileSize: 单个文件允许的最大大小。
  • maxRequestSize: 整个请求允许的最大大小。

使用 Apache Commons FileUpload

这是一个非常经典的第三方库,在 Spring 普及之前被广泛使用,它不依赖于任何框架,可以在任何 Java Web 应用中使用。

添加依赖

<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.4</version>
</dependency>
<!-- 需要 commons-io 作为依赖 -->
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.11.0</version>
</dependency>

在 Servlet 中使用

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.List;
public class CommonsFileUploadServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        // 检查是否是 multipart 请求
        if (!ServletFileUpload.isMultipartContent(request)) {
            // 如果不是,则按照普通请求处理或返回错误
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Not a multipart request");
            return;
        }
        // 配置上传参数
        DiskFileItemFactory factory = new DiskFileItemFactory();
        // 设置内存中存储数据的阈值,超过则写入临时文件
        factory.setSizeThreshold(1024 * 1024 * 1); // 1 MB
        // 设置临时文件存储目录
        File repository = (File) getServletContext().getAttribute("javax.servlet.context.tempdir");
        factory.setRepository(repository);
        ServletFileUpload upload = new ServletFileUpload(factory);
        // 设置单个文件最大大小
        upload.setFileSizeMax(1024 * 1024 * 10); // 10 MB
        // 设置总请求最大大小
        upload.setSizeMax(1024 * 1024 * 15); // 15 MB
        try {
            // 解析请求,获取所有部分
            List<FileItem> items = upload.parseRequest(request);
            for (FileItem item : items) {
                if (item.isFormField()) {
                    // 处理普通表单字段
                    String fieldName = item.getFieldName();
                    String fieldValue = item.getString("UTF-8"); // 指定编码
                    System.out.println("Field: " + fieldName + ", Value: " + fieldValue);
                } else {
                    // 处理文件
                    String fileName = new File(item.getName()).getName();
                    String uploadPath = getServletContext().getRealPath("") + File.separator + "uploads";
                    File uploadDir = new File(uploadPath);
                    if (!uploadDir.exists()) {
                        uploadDir.mkdirs();
                    }
                    File storeFile = new File(uploadPath + File.separator + fileName);
                    // 保存文件
                    item.write(storeFile);
                    System.out.println("File uploaded to: " + storeFile.getAbsolutePath());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        response.setContentType("text/plain");
        response.getWriter().println("Data processed by Commons FileUpload!");
    }
}

总结与对比

特性 Spring Boot Servlet API (原生) Apache Commons FileUpload
易用性 极高,通过注解自动绑定 中等,需要手动解析 中等,需要手动解析
依赖 spring-boot-starter-web 无额外依赖 commons-fileupload, commons-io
配置 application.properties @MultipartConfig 注解 在代码中配置 DiskFileItemFactory
功能 强大,支持复杂对象、校验等 基础功能 基础功能,非常稳定
适用场景 所有 Spring Boot 项目 不使用框架的 Java Web 项目 老项目、或不想引入 Spring 的项目
  • 如果你正在使用 Spring Boot,请毫不犹豫地选择方案一,它最简单、最安全、功能最强大。
  • 如果你在一个传统的 Java Web 项目(如使用 JSP/Servlet)中,且不想引入第三方库,方案二(Servlet API)是最佳选择
  • 如果你在一个非常老的系统中,或者有特定的需求不想使用 Servlet 3.0+ 的新特性,方案三(Commons FileUpload)是一个可靠的备选方案
分享:
扫描分享到社交APP
上一篇
下一篇