文件上傳
Action 基類屬性
public class BaseAction extends ActionSupport {
protected List upload; // 上傳文件
protected List uploadContentType; // MIME 類型
protected List uploadFileName; // 原始文件名
protected String savePath; // 保存目錄(struts.xml 注入)
// getter/setter 省略
}
上傳方法
public void uploadFiles() {
if (upload != null) {
for (int i = 0; i < upload.size(); i++) {
File src = upload.get(i);
String destName = uploadFileName.get(i);
File dest = new File(savePath, destName);
try (FileInputStream fis = new FileInputStream(src);
FileOutputStream fos = new FileOutputStream(dest)) {
byte[] buf = new byte[1024];
int len;
while ((len = fis.read(buf)) > 0) fos.write(buf, 0, len);
} catch (IOException e) {
addActionError("文件 " + destName + " 保存失敗");
}
}
}
}
struts.xml 關鍵配置
<package name="default" extends="struts-default">
<!-- 全局上傳限制 -->
<constant name="struts.multipart.maxSize" value="52428800" /> <!-- 50MB -->
<constant name="struts.multipart.saveDir" value="/tmp/uploads" />
<action name="itemRDAction_*" class="itemRDAction" method="{1}">
<param name="savePath">/data/uploads</param>
<interceptor-ref name="defaultStack">
<param name="fileUpload.allowedTypes">
image/jpeg,image/png,image/gif,
application/pdf,
application/msword,
application/vnd.openxmlformats-officedocument.wordprocessingml.document,
application/vnd.ms-excel
</param>
<param name="fileUpload.maximumSize">20971520</param> <!-- 20MB -->
</interceptor-ref>
<result name="input">/error/upload-error.jsp</result>
<result name="success">/success.jsp</result>
</action>
</package>
大文件優化與異常處理
struts.multipart.maxSize(全局)必須 ≥ Action 級 maximumSize,否則請求到達 Action 前被容器拒絕,拋出 SizeLimitExceededException。
- 在自定義
BaseAction 重寫 addActionError 友好提示:
@Override
public void addActionError(String msg) {
if (msg.contains("exceeds the configured maximum")) {
// 解析實際大小與限制大小
Pattern p = Pattern.compile("(\\d+)");
Matcher m = p.matcher(msg);
long actual = m.find() ? Long.parseLong(m.group()) : 0;
long limit = m.find() ? Long.parseLong(m.group()) : 0;
super.addActionError(String.format("上傳文件 %.2f MB 超過限制 %.2f MB", actual/1048576.0, limit/1048576.0));
} else {
super.addActionError(msg);
}
}
- 頁面用
<s:fielderror/> 顯示,或 AJAX 返回 JSON 彈窗。
文件下載
DownloadAction (Stream Result)
@Controller("downloadAction")
@Scope("prototype")
public class DownloadAction extends BaseAction {
private String filename;
private InputStream inputStream;
private String mimeType;
public InputStream getInputStream() {
try {
String path = savePath + "/" + new String(filename.getBytes("ISO-8859-1"), "UTF-8");
mimeType = ServletActionContext.getServletContext().getMimeType(path) + ";charset=UTF-8";
inputStream = new FileInputStream(path);
String agent = ServletActionContext.getRequest().getHeader("User-Agent");
if (agent != null) {
if (agent.contains("Firefox")) {
mimeType = mimeType.replace("UTF-8", "ISO-8859-1");
} else { // IE/Edge/Chrome
filename = java.net.URLEncoder.encode(filename, "UTF-8");
}
}
} catch (Exception e) {
log.error("下載準備失敗", e);
}
return inputStream;
}
public String execute() { return SUCCESS; }
// getter/setter 省略
}
struts.xml 下載配置
<action name="download" class="downloadAction">
<param name="savePath">/data/uploads</param>
<result type="stream">
<param name="contentType">${mimeType}</param>
<param name="contentDisposition">attachment;filename="${filename}"</param>
<param name="inputName">inputStream</param>
<param name="bufferSize">4096</param>
</result>
</action>
前端下載鏈接
<c:if test="${not empty item.accessory}">
<a href="download.action?filename=${item.accessory}">下載</a>
</c:if>
核心參數關係圖
| 參數 | 作用域 | 建議值 | 說明 |
| struts.multipart.maxSize | 全局 | ≥ 最大業務文件 | 容器級限制,超過直接拒絕請求 |
| fileUpload.maximumSize | Action/攔截器 | 業務允許最大 | 攔截器級限制,超過返回 input 結果 |
| fileUpload.allowedTypes | Action/攔截器 | MIME 列表 | 類型白名單,不匹配返回 input |
| struts.multipart.saveDir | 全局 | 臨時目錄 | 上傳文件落盤臨時目錄,處理完需移動/刪除 |
常見坑點總結
- 表單必須
enctype="multipart/form-data",否則進入 input 結果且無文件對象。
- 最大值層級:容器 (Tomcat
maxPostSize) → struts.multipart.maxSize → fileUpload.maximumSize,由外向內遞減。
- 編碼處理:下載文件名跨瀏覽器需區分 Firefox (ISO-8859-1) 與其他 (UTF-8 + URLEncoder)。
- 臨時文件清理:上傳失敗/異常時,
saveDir 遺留文件需定時清理任務。
- 大文件內存:Struts2 默認將上傳文件寫入臨時文件而非內存,但
maximumSize 仍限制臨時文件大小。