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

WebWork 文件上傳機制深度剖析:從 ServletDispatcher 到 JakartaMultiPartRequest 全流程解析

     編輯:Java 相關知識

前言

WebWork 文件上傳看似簡單,實則涉及請求包裝、解析器策略、臨時檔案管理等核心機制。本文從原始碼層面完整追蹤上傳流程,揭示兩次拷貝、臨時檔案命名難題、進度追蹤困境等關鍵特性。

完整上傳流程追蹤

Step 1:ServletDispatcher.service() 入口

public void service(HttpServletRequest request, HttpServletResponse response)
    throws ServletException {
    request = wrapRequest(request); // 關鍵:包裝請求
    // ...
}

Step 2:wrapRequest() 判斷與包裝

protected HttpServletRequest wrapRequest(HttpServletRequest request)
    throws IOException {
    if (MultiPartRequest.isMultiPart(request)) {
        request = new MultiPartRequestWrapper(request, getSaveDir(), getMaxSize());
    }
    return request;
}

Step 3:MultiPartRequestWrapper 建構器 - 解析器策略模式

public MultiPartRequestWrapper(HttpServletRequest request, String saveDir, int maxSize)
    throws IOException {
    // 3.1 從 webwork.properties 讀取解析器類別
    String parser = Configuration.getString("webwork.multipart.parser");
    if (parser.equals("")) {
        parser = "com.opensymphony.webwork.dispatcher.multipart.PellMultiPartRequest";
    } else if (parser.equals("pell")) { /* ... */ }
    else if (parser.equals("cos")) { /* ... */ }
    else if (parser.equals("jakarta")) { /* ... */ }

    // 3.2 反射實例化解析器
    Class baseClazz = MultiPartRequest.class;
    Class clazz = Class.forName(parser);
    Constructor ctor = clazz.getDeclaredConstructor(
        HttpServletRequest.class, String.class, int.class);
    Object[] parms = new Object[] { request, saveDir, new Integer(maxSize) };
    multi = (MultiPartRequest) ctor.newInstance(parms);
}

支援三種解析器:Pell(預設)、COS、Jakarta Commons FileUpload。

Step 4:JakartaMultiPartRequest 建構器 - 核心解析邏輯

public JakartaMultiPartRequest(HttpServletRequest servletRequest, String saveDir, int maxSize)
    throws IOException {
    DiskFileUpload upload = new DiskFileUpload();
    upload.setSizeThreshold(0);  // 強制所有上傳寫入磁碟(含表單欄位)
    upload.setSizeMax(maxSize);
    if (saveDir != null) upload.setRepositoryPath(saveDir);

    List items = upload.parseRequest(servletRequest); // 生成臨時檔案
    // upload_00000017.tmp, upload_00000018.tmp ...
}

關鍵發現:setSizeThreshold(0) 導致所有請求參數(含一般表單欄位)皆生成臨時檔案,檔名格式 upload_XXXXXXXX.tmp 難以追蹤對應關係。

後續流程

  1. 退出 wrapRequest,進入 serviceAction 啟動 Action 與攔截器堆疊
  2. 過程中刪除非上傳檔案的臨時檔案(時機不固定,疑有 daemon 執行緒清理)
  3. Action/攔截器透過 MultiPartRequestWrapper 存取上傳檔案:
MultiPartRequestWrapper wrapper = (MultiPartRequestWrapper) req;
File doc = wrapper.getFiles("doc")[0];

核心特性與限制分析

1. 上傳完成時機:Action 執行前已完成

進入 Action 堆疊時檔案已寫入臨時目錄。若需上傳進度顯示,WebWork 原生機制無法支援(臨時檔名不可預測、無進度回呼)。

2. 兩次拷貝架構

  1. 請求輸入流 → 臨時檔案(框架層完成)
  2. 臨時檔案 → 業務目標路徑(開發者自行實作)

效能開銷明顯,大檔案場景需考慮串流式處理替代方案。

3. 臨時檔案管理不確定性

  • 刪除時機不定:有時極早、有時極晚、甚至遺漏
  • 懷疑有背景執行緒清理,但非確定性機制
  • 生產環境需監控臨時目錄磁碟空間

改進建議與替代方案

  • 進度顯示需求:實作自定義 MultiPartRequest 解析器,整合進度監聽器(如 Apache Commons FileUpload ProgressListener)
  • 大檔案優化:繼承 MultiPartRequestWrapper 覆寫解析邏輯,採用串流直寫目標路徑避免二次拷貝
  • 檔名追蹤:解析階段建立原始檔名與臨時檔名映射表,供後續業務邏輯使用
  • 遷移建議:新專案建議直接使用 Servlet 3.0+ 標準 @MultipartConfig 或 Spring MVC MultipartResolver,原生支援進度、串流、配置簡化

結語

WebWork 文件上傳機制以策略模式支援多解析器、請求包裝器隔離解析細節,設計精巧但受限於臨時檔案架構,難以滿足現代進度顯示、大檔案高效傳輸需求。理解其內部流程有助於舊系統維護與遷移決策,新專案建議採用現代標準 API。

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