文件上傳是 Web 應用的基礎功能,Struts 2 通過 fileUpload 攔截器提供強大支援。本文系統講解從前端表單到後端 Action 的完整鏈路,重點攻克路徑獲取失效、大文件限制突破、類型驗證等實戰難點。
<form action="UploadFile" method="post" enctype="multipart/form-data">
<input type="file" name="source" />
<input type="submit" value="上傳">
</form>
三個必要條件:
method="post":GET 請求不支援文件傳輸enctype="multipart/form-data":編碼類型必須正確name="source":與 Action 屬性名稱對應<action name="UploadFile" class="com.example.action.UploadAction">
<result name="success">/uploadResult.jsp</result>
<!-- fileUpload 攔截器:限制大小、類型 -->
<interceptor-ref name="fileUpload">
<param name="maximumSize">20971520</param> <!-- 20MB,單位字節 -->
<param name="allowedTypes">image/jpeg,image/png,image/gif</param>
</interceptor-ref>
<!-- 必須聲明在 fileUpload 之後 -->
<interceptor-ref name="defaultStack" />
</action>
在 <struts> 根節點下添加:
<constant name="struts.multipart.maxSize" value="21000000" /> <!-- 約 20MB -->
原理:此常量控制整個應用所有上傳點的單文件最大值,Action 級別的 maximumSize 不能超過此全局值。
| 限制類型 | allowedTypes 值 | 來源 |
|---|---|---|
| 圖片 | image/bmp,image/png,image/gif,image/jpeg | %TOMCAT_HOME%/conf/web.xml |
| Word 文檔 | application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document | IANA MIME 標準 |
| application/pdf | 標準 MIME |
建議:前端 JS 先做擴展名/MIME 初步攔截,後端再做嚴格驗證,雙重保障。
假設表單 <input type="file" name="abc" />,Action 必須聲明:
private File abc; // 文件對象
private String abcFileName; // 原始文件名
private String abcContentType; // MIME 類型
命名規則:{name}、{name}FileName、{name}ContentType,Struts 2 自動注入。
常見失效代碼:
// 經常返回 null
ServletActionContext.getServletContext().getRealPath("/upload");
ServletActionContext.getRequest().getRealPath("/upload");
最佳解決方案:實現 ServletContextAware 接口
public class UploadAction extends ActionSupport implements ServletContextAware {
private ServletContext context;
@Override
public void setServletContext(ServletContext context) {
this.context = context; // Struts 2 自動注入
}
public String execute() throws IOException {
// 關鍵:斜槓不能少!
String path = context.getRealPath("/upload");
File saveDir = new File(path);
if (!saveDir.exists()) saveDir.mkdirs();
File saveFile = new File(saveDir, sourceFileName);
FileUtils.copyFile(source, saveFile); // 需 commons-io 依賴
return SUCCESS;
}
// getter/setter 省略...
}
為什麼這樣做有效?
ServletContext 注入 ActiongetRealPath("/upload") 基於 Web 應用根目錄解析,打包遷移後仍正確package com.example.action;
import java.io.File;
import java.io.IOException;
import javax.servlet.ServletContext;
import org.apache.commons.io.FileUtils;
import org.apache.struts2.util.ServletContextAware;
import com.opensymphony.xwork2.ActionSupport;
public class UploadAction extends ActionSupport implements ServletContextAware {
private static final long serialVersionUID = 1L;
private File source;
private String sourceFileName;
private String sourceContentType;
private ServletContext context;
// ServletContextAware 接口方法
@Override public void setServletContext(ServletContext context) { this.context = context; }
public ServletContext getContext() { return context; }
public void setContext(ServletContext context) { this.context = context; }
// 文件屬性 getter/setter 省略...
@Override
public String execute() throws IOException {
String path = context.getRealPath("/upload");
File saveDir = new File(path);
if (!saveDir.exists()) saveDir.mkdirs();
File saveFile = new File(saveDir, sourceFileName);
FileUtils.copyFile(source, saveFile);
return SUCCESS;
}
}
<%@ taglib uri="/struts-tags" prefix="s" %>
<p>文件名:<s:property value="sourceFileName" /></p>
<p>文件類型:<s:property value="sourceContentType" /></p>
<p>文件:<a href="upload/<s:property value="sourceFileName" />">
<s:property value="sourceFileName" /></a></p>
struts.multipart.parser 使用 Jakarta Stream 解析器提升大文件效能