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

Apache Commons FileUpload 實現檔案上傳:Servlet 完整實作與關鍵細節

     編輯:Java 相關知識

Apache Commons FileUpload 是 Java Web 專案處理 multipart/form-data 檔案上傳的經典元件。本文提供基於 Tomcat 6 的 Servlet 完整實作,涵蓋單/多檔上傳、表單欄位處理、記憶體閾值控制、大小限制、暫存目錄配置與資源清理等生產級關鍵細節。

前端表單必要條件

<form action="upload" method="post" enctype="multipart/form-data">
  <input type="file" name="myFile" /> <!-- 必須有 name 屬性 -->
  <input type="text" name="description" />
  <input type="submit" value="上傳" />
</form>
  • method="post"、enctype="multipart/form-data" 為必要條件。
  • input 標籤必須設定 name 屬性,否則無法取得內容。

Servlet 完整實作代碼

package org.xxm;

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

public class UploadFileServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        // 僅支援 POST
        return;
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        // 1. 驗證是否為 multipart 請求(同時檢查 POST)
        if (!ServletFileUpload.isMultipartContent(req)) {
            System.out.println("enctype error! 請使用 multipart/form-data");
            return;
        }

        req.setCharacterEncoding("UTF-8");

        // 2. 配置 DiskFileItemFactory
        DiskFileItemFactory factory = new DiskFileItemFactory();
        // 暫存目錄:必須真實存在且可寫入
        File repoDir = new File(getServletContext().getRealPath("/WEB-INF/tmp/upload"));
        if (!repoDir.exists()) repoDir.mkdirs();
        factory.setRepository(repoDir);
        // 記憶體閾值:超過此大小改寫入暫存檔(位元組)
        factory.setSizeThreshold(1024 * 1000); // 1MB

        // 3. 建立 ServletFileUpload 並設定大小限制
        ServletFileUpload upload = new ServletFileUpload(factory);
        upload.setFileSizeMax(100 * 1024 * 1024);      // 單檔 100MB
        upload.setSizeMax(200 * 1024 * 1024);          // 總計 200MB
        // 可選:設定標頭編碼處理中文檔名
        upload.setHeaderEncoding("UTF-8");

        List<FileItem> items = null;
        try {
            items = upload.parseRequest(req);
        } catch (FileUploadBase.SizeLimitExceededException e) {
            System.out.println("上傳總大小超過限制!");
            return;
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }

        // 4. 遍歷處理每個表單項目
        if (items != null) {
            for (FileItem item : items) {
                if (item.isFormField()) {
                    // 一般表單欄位
                    String fieldName = item.getFieldName();
                    String value = item.getString("UTF-8");
                    System.out.println("Form field: " + fieldName + " = " + value);
                } else {
                    // 檔案欄位
                    String originalName = item.getName();
                    if (originalName == null || originalName.isEmpty()) continue;

                    // 處理不同瀏覽器傳送的完整路徑,僅保留檔名
                    String fileName = originalName.substring(originalName.lastIndexOf("\\") + 1);
                    // 防禦性處理 Unix 路徑分隔符
                    fileName = fileName.substring(fileName.lastIndexOf("/") + 1);

                    // 目標儲存目錄:專案外部絕對路徑,避免戰爭重新部署遺失
                    File uploadDir = new File(getServletContext().getRealPath("/WEB-INF/upload"));
                    if (!uploadDir.exists()) uploadDir.mkdirs();
                    File destFile = new File(uploadDir, fileName);

                    try (InputStream in = item.getInputStream();
                         OutputStream out = new BufferedOutputStream(new FileOutputStream(destFile))) {
                        Streams.copy(in, out, true); // true = 關閉輸入流
                        System.out.println("檔案上傳成功:" + destFile.getAbsolutePath());
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

關鍵配置參數詳細說明

參數設定方法建議值/說明
暫存目錄factory.setRepository(File)專案外 /tmp/upload,確保磁碟空間充足
記憶體閾值factory.setSizeThreshold(int)1MB (1024*1000);小檔留記憶體,大檔落地
單檔上限upload.setFileSizeMax(long)依業務需求,例 100MB
總上傳上限upload.setSizeMax(long)略大於單檔上限,防單一請求耗盡資源
標頭編碼upload.setHeaderEncoding(String)UTF-8,解決中文檔名亂碼

生產環境避坑指南

  1. 暫存檔清理:DiskFileItemFactory 不會自動刪除暫存檔,需排程清理 repository 目錄,或實作 FileCleaningTracker。
  2. 檔名衝突:建議重命名為 UUID + 原始副檔名,避免覆蓋與路徑遍歷攻擊。
  3. 惡意檔案防護:驗證 Content-Type、副檔名、魔數;掃描病毒;儲存於非 Web 可直接存取目錄。
  4. 大檔斷點續傳:FileUpload 不支援,需前端分片(Plupload/WebUploader)+ 後端合併。
  5. 記憶體洩漏觀察:長時間運行若記憶體不釋放,檢查 Streams.copy 是否正確關閉流、暫存檔是否清理。

總結

Commons FileUpload 配置得當可穩定支援中小型檔案上傳。核心在於:正確設定 DiskFileItemFactory 暫存策略、合理限制大小、妥善處理編碼與資源關閉。大檔、高併發、斷點續傳場景建議引入專業上傳元件或雲端儲存服務(OSS/S3)。

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