JSP 文件上传

multipart 表单、Part 读取、保存路径与安全校验。

Intermediate
Key Points表单要 multipart/form-data,页面要 @MultipartConfig(或 web.xml 配置),用 Part 读字节并重命名落盘。

表单

JSP
<form action="upload.jsp" method="post" enctype="multipart/form-data">
  <input type="file" name="file">
  <input type="text" name="note" placeholder="说明">
  <button>上传</button>
</form>

Of the three enctype values only multipart/form-data sends file bytes; the default application/x-www-form-urlencoded sends just the name.

接收

JSP
<%@ page contentType="text/html;charset=UTF-8"
         import="java.io.*, java.nio.file.*, java.util.UUID" %>
<%
    request.setCharacterEncoding("UTF-8");
    Part part = request.getPart("file");
    String name = part.getSubmittedFileName();

    if (name == null || name.isBlank()) {
        out.println("<p>没有选择文件</p>");
        return;
    }

    String ext = name.substring(name.lastIndexOf('.')).toLowerCase();
    if (!java.util.Set.of(".jpg", ".png", ".pdf").contains(ext)) {
        response.sendError(400, "不支持的文件类型");
        return;
    }

    Path dir = Path.of(application.getRealPath("/uploads"));
    Files.createDirectories(dir);
    Path target = dir.resolve(UUID.randomUUID() + ext);
    part.write(target.toString());

    out.println("<p>已保存:" + target.getFileName() + "</p>");
%>
Output
已保存:3f9c1b2a-7d4e-4c88-a1b0-6e2d5f7a8c91.pdf

配置大小限制

XML
<servlet>
  <servlet-name>upload</servlet-name>
  <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
  <multipart-config>
    <max-file-size>5242880</max-file-size>        <!-- 5MB -->
    <max-request-size>20971520</max-request-size> <!-- 20MB -->
    <file-size-threshold>1048576</file-size-threshold>
  </multipart-config>
</servlet>

A JSP cannot use the @MultipartConfig annotation (that is for servlets). Either declare multipart-config in web.xml or move the upload into a servlet and use the JSP only for display — the latter is cleaner.

三条安全规则

  1. 重命名 —— 原文件名可能是 ../../web.xml,直接用会覆盖任意文件
  2. 白名单扩展名 —— 黑名单永远漏,只允许明确需要的类型
  3. 别放在能执行脚本的目录 —— 上传目录能跑 JSP 就等于开了后门
application.getRealPath() returns null when the war is not exploded. In production store files outside the web root (e.g. /data/uploads) and serve them through a download servlet — which also gives you access control.

大文件

Part.write() stages the whole file in a temp directory, doubling disk for GB uploads. Use a streaming parser (Commons FileUpload Streaming API) to read and write in chunks.

Friends & Partners