首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> JSP 程式設計 >> JSP 入門

JSP 檔案上傳完整實作 (Apache Commons FileUpload)

     編輯:JSP 入門

JSP 檔案上傳完整實作

使用 Apache Commons FileUpload 元件處理 multipart/form-data 請求,是傳統 JSP/Servlet 專案上傳檔案的標準做法。本文涵蓋前端表單、後端 Servlet、依賴管理、限制設定與安全考量。


專案結構與依賴

WebApp/
├─ src/
│  └─ com/example/UploadServlet.java
├─ WebContent/
│  ├─ upload.jsp          (上傳表單)
│  ├─ result.jsp          (結果頁)
│  └─ WEB-INF/
│     ├─ lib/
│     │  ├─ commons-fileupload-1.4.jar
│     │  └─ commons-io-2.11.0.jar
│     └─ web.xml

Maven 依賴:

<dependency>
  <groupId>commons-fileupload</groupId>
  <artifactId>commons-fileupload</artifactId>
  <version>1.4</version>
</dependency>
<dependency>
  <groupId>commons-io</groupId>
  <artifactId>commons-io</artifactId>
  <version>2.11.0</version>
</dependency>

前端上傳表單 (upload.jsp)

<%@ page contentType="text/html;charset=UTF-8" %>
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>檔案上傳</title></head>
<body>
  <h2>檔案上傳表單</h2>
  <form action="UploadServlet" method="post" enctype="multipart/form-data">
    <p>選擇檔案:<input type="file" name="uploadFile" required></p>
    <p>說明:<input type="text" name="description"></p>
    <p><input type="submit" value="上傳"></p>
  </form>
</body></html>

關鍵屬性

  • method="post":GET 無法傳送檔案內容
  • enctype="multipart/form-data":必要,瀏覽器才會正確編碼檔案二進位資料
  • name="uploadFile":Servlet 端用此名稱取得 FileItem

後端 Servlet 實作 (UploadServlet.java)

package com.example;

import jakarta.servlet.*;
import jakarta.servlet.annotation.*;
import jakarta.servlet.http.*;
import org.apache.commons.fileupload.*;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import java.io.*;
import java.util.*;

@WebServlet("/UploadServlet")
@MultipartConfig // Servlet 3.0+ 也可用原生支援,但 FileUpload 更彈性
public class UploadServlet extends HttpServlet {

  private static final long serialVersionUID = 1L;
  private static final String UPLOAD_DIR = "uploads"; // 相對於 webapp 根目錄
  private static final int MEM_THRESHOLD = 1024 * 1024 * 3;   // 3MB
  private static final int MAX_FILE_SIZE = 1024 * 1024 * 50;  // 50MB
  private static final int MAX_REQUEST_SIZE = 1024 * 1024 * 60; // 60MB

  @Override
  protected void doPost(HttpServletRequest req, HttpServletResponse resp) 
      throws ServletException, IOException {

    // 1. 檢查是否為 multipart 請求
    if (!ServletFileUpload.isMultipartContent(req)) {
      resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "請求必須為 multipart/form-data");
      return;
    }

    // 2. 配置工廠與上傳處理器
    DiskFileItemFactory factory = new DiskFileItemFactory();
    factory.setSizeThreshold(MEM_THRESHOLD);
    factory.setRepository(new File(System.getProperty("java.io.tmpdir")));

    ServletFileUpload upload = new ServletFileUpload(factory);
    upload.setFileSizeMax(MAX_FILE_SIZE);
    upload.setSizeMax(MAX_REQUEST_SIZE);
    upload.setHeaderEncoding("UTF-8"); // 解決檔名中文亂碼

    // 3. 決定儲存路徑 (真實部署建議放在 Web 根目錄外)
    String uploadPath = getServletContext().getRealPath("") + File.separator + UPLOAD_DIR;
    File uploadDir = new File(uploadPath);
    if (!uploadDir.exists()) uploadDir.mkdirs();

    String message = "";
    try {
      // 4. 解析請求
      List items = upload.parseRequest(req);
      for (FileItem item : items) {
        if (item.isFormField()) {
          // 一般表單欄位
          String fieldName = item.getFieldName();
          String value = item.getString("UTF-8");
          System.out.println(fieldName + " = " + value);
        } else {
          // 檔案欄位
          String originalName = new File(item.getName()).getName(); // 防止路徑遍歷
          String savedName = System.currentTimeMillis() + "_" + originalName; // 避免同名覆蓋
          File saveFile = new File(uploadPath, savedName);
          item.write(saveFile);
          message = "檔案「" + originalName + "」上傳成功!儲存為:" + savedName;
        }
      }
    } catch (FileUploadException e) {
      message = "上傳失敗:" + e.getMessage();
    } catch (Exception e) {
      message = "伺服器錯誤:" + e.getMessage();
      e.printStackTrace();
    }

    // 5. 轉向結果頁
    req.setAttribute("message", message);
    req.getRequestDispatcher("/result.jsp").forward(req, resp);
  }
}

結果頁 (result.jsp)

<%@ page contentType="text/html;charset=UTF-8" %>
<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title>上傳結果</title></head>
<body>
  <h2><%= request.getAttribute("message") %></h2>
  <p><a href="upload.jsp">繼續上傳</a></p>
</body></html>

web.xml 設定 (若不使用 @WebServlet)

<servlet>
  <servlet-name>UploadServlet</servlet-name>
  <servlet-class>com.example.UploadServlet</servlet-class>
</servlet>
<servlet-mapping>
  <servlet-name>UploadServlet</servlet-name>
  <url-pattern>/UploadServlet</url-pattern>
</servlet-mapping>

安全與最佳實踐

風險對策
惡意檔案類型驗證 Content-Type、副檔名、魔數;僅允許白名單類型
檔名路徑遍歷使用 new File(item.getName()).getName() 取得純檔名
超大檔案攻擊設定 setFileSizeMax、setSizeMax、Nginx client_max_body_size
儲存路徑可直接下載檔案存放在 WEB-INF 外部或資料庫,提供下載 Servlet 控制權限
覆蓋既有檔案重新命名 (UUID、時間戳、雜湊)
中文檔名亂碼upload.setHeaderEncoding("UTF-8")、Tomcat URIEncoding="UTF-8"

Servlet 3.0+ 原生支援 (替代方案)

若不想引入第三方 JAR,可用 @MultipartConfig + request.getPart():

@WebServlet("/UploadServlet")
@MultipartConfig(
  location = "/tmp",           // 暫存目錄
  maxFileSize = 50_000_000,    // 50MB
  maxRequestSize = 60_000_000, // 60MB
  fileSizeThreshold = 3_000_000 // 3MB
)
public class UploadServlet extends HttpServlet {
  protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    Part filePart = req.getPart("uploadFile");
    String fileName = Paths.get(filePart.getSubmittedFileName()).getFileName().toString();
    filePart.write(uploadPath + File.separator + fileName);
    // ...
  }
}
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved