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

Commons FileUpload 文件上傳入門範例:從依賴引入到 Servlet 完整實現

     編輯:Java 相關知識

Apache Commons FileUpload 是 Java Web 文件上傳的經典組件,雖然 Servlet 3.0+ 提供了原生 @MultipartConfig 支援,但理解 FileUpload 核心 API 對維護老項目、適配非標準容器、實現自定義解析邏輯仍有價值。本文提供最小依賴、最簡代碼的完整上傳範例,涵蓋工廠配置、流式解析、UUID 命名、大小限制、異常處理。

Maven 依賴 (兩個核心 JAR)

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

前端上傳頁面

<html>
<body>
<h2>文件上傳演示</h2>
<form action="upload" method="post" enctype="multipart/form-data">
    <p>選擇文件: <input type="file" name="file" /></p>
    <p><input type="submit" value="上傳" /></p>
</form>
</body>
</html>

核心 Servlet 實現

@WebServlet("/upload")
public class FileUploadServlet extends HttpServlet {
    private static final String UPLOAD_DIR = "C:/temp/uploads"; // Windows 路徑示例
    private static final long MAX_FILE_SIZE = 1024 * 1024; // 1MB

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter out = resp.getWriter();

        try {
            // 1. 創建解析器
            ServletFileUpload upload = new ServletFileUpload();
            upload.setFileSizeMax(MAX_FILE_SIZE);
            // 也可設置總請求大小: upload.setSizeMax(10 * 1024 * 1024);

            // 2. 獲取迭代器 (低內存模式,適合大文件)
            FileItemIterator iterator = upload.getItemIterator(req);
            
            while (iterator.hasNext()) {
                FileItemStream item = iterator.next();
                
                if (!item.isFormField()) {
                    // 3. 處理文件流
                    handleFileItem(item);
                } else {
                    // 處理普通表單字段 (可選)
                    String fieldName = item.getFieldName();
                    String value = Streams.asString(item.openStream(), StandardCharsets.UTF_8);
                    System.out.println("表單字段: " + fieldName + " = " + value);
                }
            }
            
            // 4. 轉發成功頁面
            req.getRequestDispatcher("success.jsp").forward(req, resp);
            
        } catch (FileUploadException e) {
            throw new ServletException("文件上傳解析失敗", e);
        }
    }

    private void handleFileItem(FileItemStream item) throws IOException {
        String originalName = item.getName();
        System.out.println("接收文件: " + originalName);
        
        // 1. 生成唯一文件名 (UUID + 原擴展名)
        String ext = "";
        int dot = originalName.lastIndexOf('.');
        if (dot > 0) ext = originalName.substring(dot);
        String savedName = UUID.randomUUID().toString() + ext;
        
        // 2. 確保目錄存在
        File dir = new File(UPLOAD_DIR);
        if (!dir.exists()) dir.mkdirs();
        
        File target = new File(dir, savedName);
        
        // 3. 流式寫入 (4KB 緩衝區)
        byte[] buffer = new byte[4096];
        try (InputStream in = item.openStream();
             OutputStream out = new BufferedOutputStream(new FileOutputStream(target))) {
            
            int len;
            while ((len = in.read(buffer)) != -1) {
                out.write(buffer, 0, len);
            }
        }
        System.out.println("文件保存至: " + target.getAbsolutePath());
    }
}

web.xml 配置 (Servlet 2.5 風格,可選)

<web-app version="2.5" xmlns="https://java.sun.com/xml/ns/javaee">
    <servlet>
        <servlet-name>FileUploadServlet</servlet-name>
        <servlet-class>com.example.FileUploadServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>FileUploadServlet</servlet-name>
        <url-pattern>/upload</url-pattern>
    </servlet-mapping>
</web-app>

成功頁面

<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<body>
<h3>✅ 文件上傳成功!</h3>
<p><a href="upload.html">繼續上傳</a></p>
</body>
</html>

關鍵配置參數詳解

參數設置方法默認值建議值
單文件最大值upload.setFileSizeMax()無限制10-100MB
總請求最大值upload.setSizeMax()無限制單文件 × 最大文件數
內存閾值factory.setSizeThreshold()10KB1-5MB
臨時目錄factory.setRepository()java.io.tmpdir項目外專用目錄
文件名編碼upload.setHeaderEncoding()平台默認UTF-8

兩種解析模式對比

// 模式一:List parseRequest(req) - 全量載入內存
List<FileItem> items = upload.parseRequest(req);
for (FileItem item : items) {
    item.write(new File(target)); // 一次性寫入
}
// 適用:小文件、高併發、需多次讀取流

// 模式二:FileItemIterator getItemIterator(req) - 流式處理 (本文採用)
FileItemIterator it = upload.getItemIterator(req);
while (it.hasNext()) {
    FileItemStream item = it.next();
    try (InputStream in = item.openStream()) { ... }
}
// 適用:大文件、低內存、單次處理

常見異常處理

try {
    FileItemIterator it = upload.getItemIterator(req);
    // ...
} catch (FileUploadException e) {
    if (e instanceof SizeLimitExceededException) {
        out.println("❌ 文件大小超過限制 (" + MAX_FILE_SIZE/1024/1024 + "MB)");
    } else if (e instanceof FileSizeLimitExceededException) {
        out.println("❌ 單個文件超過限制");
    } else {
        out.println("❌ 上傳失敗: " + e.getMessage());
    }
    e.printStackTrace();
}

生產環境遷移建議

  1. Servlet 3.0+ 環境:優先使用 @MultipartConfig + request.getPart() 原生 API,零依賴
  2. Spring Boot 項目:直接使用 MultipartFile + spring.servlet.multipart 配置
  3. 大文件/斷點續傳:引入 Resumable.js/Uppy 前端 + 分片合併後端
  4. 雲原生部署:文件直傳 OSS/S3/MinIO,後端僅簽名授權

總結

Commons FileUpload 核心流程:ServletFileUpload 解析器 → FileItemIterator 流式迭代 → 區分表單/文件字段 → InputStream 流式寫入目標存儲。本文示例採用迭代器模式,內存佔用恆定 (4KB 緩衝區),適合大文件上傳場景。關鍵生產級細節:UUID 防重名、編碼設置防亂碼、大小限制防攻擊、臨時目錄隔離、異常分類處理。理解這套機制,能輕鬆遷移至現代框架或實現自定義存儲邏輯。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved