Servlet 文件上传

一、什么是 Servlet 文件上传?

Servlet 文件上传允许用户通过 Web 表单将文件(如图片、文档)上传到服务器。这在实际应用中很常见,例如用户头像上传或附件提交。HTTP 协议本身不支持直接上传文件,因此需要使用 multipart/form-data 编码的表单。Servlet 可以通过第三方库(如 Apache Commons FileUpload)或 Servlet 3.0+ 的内置 API 来处理上传。

上传过程涉及:

  • 客户端:HTML 表单提交文件。
  • 服务器:解析请求,保存文件到指定目录。

二、准备工作

  1. HTML 表单
  • 表单必须使用 method="post"enctype="multipart/form-data"
  • 输入字段使用 <input type="file">

示例表单(upload.html)

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>文件上传</title>
</head>
<body>
    <form action="UploadServlet" method="post" enctype="multipart/form-data">
        <label>选择文件:</label>
        <input type="file" name="file"><br>
        <input type="submit" value="上传">
    </form>
</body>
</html>
  1. 依赖
  • 对于传统方式:添加 Apache Commons FileUpload 和 Commons IO JAR 包(下载自 Apache 官网)。
  • 对于 Servlet 3.0+:无需额外依赖,但需在 web.xml 中指定 Servlet 版本为 3.0 或更高。
  1. 服务器目录:创建存储上传文件的目录,如 WEB-INF/uploads,确保有写权限。

三、使用 Apache Commons FileUpload(传统方式)

适用于 Servlet 2.5 或更早版本。需要解析请求中的 multipart 数据。

代码示例(UploadServlet.java)

import javax.servlet.http.*;
import javax.servlet.*;
import java.io.*;
import java.util.*;
import org.apache.commons.fileupload.*;
import org.apache.commons.fileupload.disk.*;
import org.apache.commons.fileupload.servlet.*;

public class UploadServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();

        // 检查是否为 multipart 请求
        boolean isMultipart = ServletFileUpload.isMultipartContent(request);
        if (!isMultipart) {
            out.println("不是文件上传请求!");
            return;
        }

        // 配置上传参数
        DiskFileItemFactory factory = new DiskFileItemFactory();
        // 设置内存阈值 - 超过后将产生临时文件并存储于临时目录中
        factory.setSizeThreshold(1024 * 1024); // 1MB
        // 设置临时存储目录
        factory.setRepository(new File(System.getProperty("java.io.tmpdir")));

        ServletFileUpload upload = new ServletFileUpload(factory);
        // 设置最大文件上传值
        upload.setFileSizeMax(1024 * 1024 * 10); // 10MB
        // 设置最大请求值 (包含文件和表单项)
        upload.setSizeMax(1024 * 1024 * 50); // 50MB

        // 解析请求
        try {
            List<FileItem> items = upload.parseRequest(request);
            for (FileItem item : items) {
                if (item.isFormField()) {
                    // 处理普通表单字段
                    String name = item.getFieldName();
                    String value = item.getString("UTF-8"); // 支持中文
                    out.println("表单字段: " + name + " = " + value);
                } else {
                    // 处理文件上传
                    String fileName = new File(item.getName()).getName();
                    String filePath = getServletContext().getRealPath("/uploads") + File.separator + fileName;
                    File uploadedFile = new File(filePath);
                    item.write(uploadedFile);
                    out.println("文件上传成功: " + fileName);
                }
            }
        } catch (Exception e) {
            out.println("上传失败: " + e.getMessage());
        }
    }
}

说明

  • ServletFileUpload.parseRequest(request):解析请求,获取 FileItem 列表。
  • item.isFormField():区分普通字段和文件字段。
  • item.write(file):将文件写入服务器。
  • 支持多个文件:表单中添加多个 <input type="file">

四、使用 Servlet 3.0+ 内置支持

Servlet 3.0 引入 @MultipartConfig 注解和 Part 接口,简化上传过程。

代码示例(UploadServlet.java)

import javax.servlet.http.*;
import javax.servlet.*;
import javax.servlet.annotation.*;
import java.io.*;

@MultipartConfig(
    fileSizeThreshold = 1024 * 1024,    // 1MB
    maxFileSize = 1024 * 1024 * 10,     // 10MB
    maxRequestSize = 1024 * 1024 * 50   // 50MB
)
public class UploadServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();

        // 获取文件 Part
        Part filePart = request.getPart("file"); // "file" 是表单字段名
        if (filePart != null) {
            // 获取文件名
            String fileName = getFileName(filePart);
            // 保存文件
            String filePath = getServletContext().getRealPath("/uploads") + File.separator + fileName;
            filePart.write(filePath);
            out.println("文件上传成功: " + fileName);
        } else {
            out.println("未选择文件!");
        }
    }

    // 提取文件名
    private String getFileName(Part part) {
        String header = part.getHeader("content-disposition");
        String fileName = header.substring(header.lastIndexOf("=") + 2, header.length() - 1);
        return new File(fileName).getName();
    }
}

说明

  • @MultipartConfig:配置上传限制,如文件大小。
  • request.getPart("file"):获取文件 Part 对象。
  • part.write(path):直接写入文件。
  • 支持多个文件:使用 request.getParts() 遍历。

在 web.xml 中注册 Servlet(如果不使用注解):

<servlet>
    <servlet-name>UploadServlet</servlet-name>
    <servlet-class>com.example.UploadServlet</servlet-class>
    <multipart-config>
        <max-file-size>10485760</max-file-size> <!-- 10MB -->
    </multipart-config>
</servlet>
<servlet-mapping>
    <servlet-name>UploadServlet</servlet-name>
    <url-pattern>/UploadServlet</url-pattern>
</servlet-mapping>

五、处理中文文件名和内容

上传文件时,中文文件名可能乱码。解决方案:

  1. 请求编码request.setCharacterEncoding("UTF-8")
  2. 提取文件名:在 getFileName() 方法中,使用 new String(fileName.getBytes("ISO-8859-1"), "UTF-8") 转码(如果必要)。
  3. 文件内容:如果文件是文本,确保读取时指定 UTF-8。

示例修改(提取文件名支持中文)

private String getFileName(Part part) {
    String header = part.getHeader("content-disposition");
    String fileName = header.substring(header.lastIndexOf("=") + 2, header.length() - 1);
    try {
        fileName = new String(fileName.getBytes("ISO-8859-1"), "UTF-8");
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }
    return new File(fileName).getName();
}

六、注意事项

  1. 安全性
  • 验证文件类型和大小,防止恶意上传(如可执行文件)。
  • 使用 UUID 重命名文件,避免覆盖和路径注入。
  1. 性能
  • 大文件上传使用临时目录,避免内存溢出。
  • 生产环境使用云存储(如阿里云 OSS)代替本地目录。
  1. 异常处理
  • 捕获 FileUploadExceptionSizeLimitExceededException 处理超限情况。
  • 确保目录存在,否则创建:new File(filePath).getParentFile().mkdirs()
  1. 浏览器兼容
  • 测试不同浏览器(如 Chrome、IE),IE 可能需要额外处理文件名。
  1. 多个文件
  • 表单添加多个 type="file",服务器使用循环处理。

七、总结

Servlet 文件上传可以通过 Commons FileUpload 或 Servlet 3.0+ 的内置 API 实现,前者兼容旧版本,后者更简洁高效。重点在于正确配置表单、处理 multipart 请求,并注意中文编码和安全问题。在实际开发中,可结合 JSP 或框架如 Spring MVC 来扩展功能。

如果需要下载功能讲解或更多示例,请继续提问!

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注