杰瑞科技汇

java html 上传文件

  1. 后端: 使用 Java 代码(通常是 Servlet)来接收和处理从客户端上传的文件。
  2. 前端: 使用 HTML 的 <input type="file"> 元素来让用户选择文件,并通过表单提交。

下面我将提供一个完整、分步的示例,包括前端 HTML 和后端 Java Servlet 代码。

java html 上传文件-图1
(图片来源网络,侵删)

核心概念

  1. <form>:

    • method="post": 文件上传必须使用 POST 方法,因为 GET 方法有 URL 长度限制且不安全。
    • enctype="multipart/form-data": 这是最关键的一步,它告诉浏览器要对表单数据进行特殊编码,以便能够以二进制流的形式上传文件,而不是将文件内容编码到 URL 中。
  2. <input type="file">: 创建一个文件选择框,用户可以在此选择本地文件。

  3. 后端处理:

    • 浏览器发送 multipart/form-data 格式的请求,后端无法像普通表单数据那样用 request.getParameter() 来获取文件。
    • 我们需要使用一个专门的库来解析这种复杂的请求体。Apache Commons FileUpload 是最经典和常用的选择,现在更推荐其升级版 Apache Commons FileUpload2 或 Servlet API 3.0+ 内置的支持。

使用 Apache Commons FileUpload (经典方案,适用于 Servlet 2.x 和 3.x)

这个方案兼容性好,即使你的项目运行在较旧的 Servlet 容器中也能使用。

java html 上传文件-图2
(图片来源网络,侵删)

步骤 1: 添加依赖

如果你使用 Maven,在 pom.xml 中添加以下依赖:

<dependencies>
    <!-- Servlet API -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</version>
        <scope>provided</scope>
    </dependency>
    <!-- Apache Commons FileUpload -->
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload2-jakarta</artifactId>
        <version>2.0.0-M2</version>
    </dependency>
    <!-- Commons IO (FileUpload 依赖它) -->
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.11.0</version>
    </dependency>
</dependencies>

注意: commons-fileupload2-jakarta 是 Jakarta EE 版本,适用于 Tomcat 10+ 等现代 Servlet 容器,如果你的项目是传统的 Java EE (Tomcat 9-),请使用 commons-fileupload2 (非 jakarta)。

步骤 2: 创建前端 HTML (upload.html)

创建一个简单的 HTML 文件,包含一个文件上传表单。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">文件上传示例</title>
    <style>
        body { font-family: sans-serif; margin: 20px; }
        .container { max-width: 600px; margin: auto; padding: 20px; border: 1px solid #ccc; border-radius: 8px; }
        input[type="file"] { margin-bottom: 10px; }
        input[type="submit"] { padding: 10px 15px; background-color: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; }
    </style>
</head>
<body>
    <div class="container">
        <h2>上传文件</h2>
        <form action="upload" method="post" enctype="multipart/form-data">
            <!-- 
              name="file" 必须与后端代码中获取的 field name 一致
              multiple="multiple" 允许多选
            -->
            <input type="file" name="file" multiple required>
            <br>
            <input type="submit" value="上传">
        </form>
    </div>
</body>
</html>

步骤 3: 创建后端 Servlet (FileUploadServlet.java)

这是处理文件上传的核心逻辑。

java html 上传文件-图3
(图片来源网络,侵删)
import org.apache.commons.fileupload2.core.DiskFileItem;
import org.apache.commons.fileupload2.core.FileItem;
import org.apache.commons.fileupload2.core.FileItemFactory;
import org.apache.commons.fileupload2.jakarta.servlet6.JakartaServletDiskFileUpload;
import org.apache.commons.fileupload2.jakarta.servlet6.JakartaServletFileUpload;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
@WebServlet("/upload") // 将 Servlet 映射到 /upload 路径
public class FileUploadServlet extends HttpServlet {
    // 上传文件存储目录 (在 webapp 目录下创建一个 uploads 文件夹)
    private static final String UPLOAD_DIRECTORY = "uploads";
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 检查请求是否包含 multipart
        boolean isMultipart = JakartaServletFileUpload.isMultipartContent(request);
        if (!isMultipart) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "请求必须是 multipart/form-data 类型");
            return;
        }
        // 配置上传参数
        DiskFileItemFactory factory = new DiskFileItemFactory();
        // 设置内存中存储文件大小的阈值 (1MB)
        factory.setSizeThreshold(1024 * 1024);
        // 设置临时目录
        File tempDir = (File) getServletContext().getAttribute("jakarta.servlet.context.tempdir");
        factory.setRepository(tempDir);
        JakartaServletDiskFileUpload upload = new JakartaServletDiskFileUpload(factory);
        // 设置最大文件大小 (10MB)
        upload.setFileSizeMax(1024 * 1024 * 10);
        // 设置整个请求的最大大小 (20MB)
        upload.setSizeMax(1024 * 1024 * 20);
        // 解析请求,获取文件项列表
        List<FileItem> formItems;
        try {
            formItems = upload.parseRequest(request);
        } catch (Exception e) {
            e.printStackTrace();
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "解析请求失败: " + e.getMessage());
            return;
        }
        // 处理上传的文件
        for (FileItem item : formItems) {
            // 检查当前 item 是否是普通表单字段还是文件
            if (!item.isFormField()) {
                String fileName = new File(item.getName()).getName();
                String filePath = getServletContext().getRealPath("") + File.separator + UPLOAD_DIRECTORY + File.separator + fileName;
                // 确保上传目录存在
                File uploadDir = new File(getServletContext().getRealPath("") + File.separator + UPLOAD_DIRECTORY);
                if (!uploadDir.exists()) {
                    uploadDir.mkdir();
                }
                // 保存文件到服务器
                try {
                    item.write(new File(filePath));
                    System.out.println("文件 " + fileName + " 已成功上传到 " + filePath);
                } catch (Exception e) {
                    e.printStackTrace();
                    response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "保存文件失败: " + e.getMessage());
                    return;
                }
            }
        }
        // 上传成功后,重定向到成功页面
        response.sendRedirect("success.html");
    }
}

步骤 4: 创建成功页面 (success.html)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">上传成功</title>
</head>
<body>
    <h1>文件上传成功!</h1>
    <p><a href="upload.html">返回上传页面</a></p>
</body>
</html>

使用 Servlet 3.0+ 内置 API (现代方案)

如果你使用的是 Servlet 3.0 或更高版本(Tomcat 7+, Jetty 8+, WildFly 8+ 等),可以直接使用 Servlet API 内置的功能,无需引入第三方库。

步骤 1 & 2: 前端 HTML 和依赖

前端 HTML (upload.html) 和 Maven 依赖中的 Servlet API 部分与方案一完全相同。

步骤 3: 创建后端 Servlet (ModernFileUploadServlet.java)

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.MultipartConfig;
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.PrintWriter;
import java.util.Collection;
@WebServlet("/upload-modern")
// 使用 @MultipartConfig 注解来配置文件上传
@MultipartConfig(
    fileSizeThreshold = 1024 * 1024,       // 1 MB 内存缓存阈值
    maxFileSize = 1024 * 1024 * 10,        // 单个文件最大 10MB
    maxRequestSize = 1024 * 1024 * 20     // 整个请求最大 20MB
)
public class ModernFileUploadServlet extends HttpServlet {
    private static final String UPLOAD_DIRECTORY = "uploads";
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 确保上传目录存在
        String uploadPath = getServletContext().getRealPath("") + File.separator + UPLOAD_DIRECTORY;
        File uploadDir = new File(uploadPath);
        if (!uploadDir.exists()) {
            uploadDir.mkdir();
        }
        // 获取所有上传的 Part
        Collection<Part> parts = request.getParts();
        for (Part part : parts) {
            // 获取文件名
            String fileName = getSubmittedFileName(part);
            if (fileName != null && !fileName.isEmpty()) {
                // 构建服务器上的文件路径
                String filePath = uploadPath + File.separator + fileName;
                // 将 Part 写入文件
                part.write(filePath);
                System.out.println("文件 " + fileName + " 已成功上传到 " + filePath);
            }
        }
        response.sendRedirect("success.html");
    }
    /**
     * 辅助方法:从 Part 对象中提取文件名
     */
    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(File.separator) + 1); // 处理不同操作系统的路径分隔符
            }
        }
        return null;
    }
}

注意: 这里我们使用了 @WebServlet("/upload-modern")@MultipartConfig 注解,使代码更加简洁。


总结与对比

特性 方案一 (Commons FileUpload) 方案二 (Servlet 3.0+)
依赖 需要添加 commons-fileuploadcommons-io 无需额外依赖,只需 Servlet API
代码复杂度 较高,需要手动解析请求、创建工厂、设置参数 非常低,使用注解 @MultipartConfig 即可
兼容性 非常好,适用于所有 Servlet 2.x 和 3.x+ 有限,仅适用于 Servlet 3.0 及以上版本
推荐度 对于旧项目或需要精细控制时使用 强烈推荐,对于新项目,这是最标准、最简洁的方式

重要注意事项

  1. 安全性:

    • 文件名: 用户上传的文件名可能包含恶意字符或路径遍历攻击(如 ../../../malicious.txt),在上传前,务必对文件名进行清理和验证。
    • 文件类型: 不要仅依赖文件扩展名,检查文件的“魔数”(Magic Numbers)或使用专门的库(如 Apache Tika)来验证文件的真实类型,防止上传恶意脚本(如 .jsp, .php)。
    • 病毒扫描: 对于生产环境,上传的文件应经过病毒扫描。
    • 存储位置: 不要将文件存储在 Web 服务器的根目录下,以防止直接访问,最好放在 WEB-INF 目录或其子目录下,这样它们就不能通过 URL 直接访问了。
  2. 性能:

    • 对于非常大的文件,内存和临时文件的处理方式会影响性能。@MultipartConfigFileUpload 都允许你配置内存缓存大小和临时目录。
  3. 配置:

    • web.xml 中配置 max-file-sizemax-request-size(如果不用注解)也是一个好习惯,作为双重保障。

希望这个详细的指南能帮助你成功实现 Java Web 文件上传功能!

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