Struts2 通過 FileUploadInterceptor 處理 multipart/form-data 請求。當多個 <input type="file" name="upload"> 共用同一 name 時,框架自動將其綁定為三個同名數組屬性:
File[] upload — 臨時文件對象數組String[] uploadFileName — 原始文件名數組String[] uploadContentType — MIME 類型數組三個數組長度一致,索引對應同一文件。
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head><title>多文件上傳演示</title></head>
<body>
<h3>選擇多張圖片上傳</h3>
<form action="upload.action" method="post" enctype="multipart/form-data">
文件標題:<input type="text" name="title" /><br/>
選擇文件一:<input type="file" name="upload" /><br/>
選擇文件二:<input type="file" name="upload" /><br/>
選擇文件三:<input type="file" name="upload" /><br/>
<input type="submit" value="上傳" />
</form>
</body>
</html>
package com.example.action;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.commons.io.FileUtils;
import org.apache.struts2.ServletActionContext;
import java.io.File;
import java.io.IOException;
public class UploadAction extends ActionSupport {
// 表單普通字段
private String title;
// 文件上傳三要素數組(名稱必須與 JSP name="upload" 對應)
private File[] upload;
private String[] uploadFileName;
private String[] uploadContentType;
// 保存路徑(由 struts.xml 注入)
private String savePath;
// Getter/Setter 省略...
@Override
public String execute() throws Exception {
if (upload == null || upload.length == 0) {
addFieldError("upload", "請至少選擇一個文件");
return INPUT;
}
String realPath = ServletActionContext.getServletContext().getRealPath(savePath);
File destDir = new File(realPath);
if (!destDir.exists()) destDir.mkdirs();
for (int i = 0; i < upload.length; i++) {
File src = upload[i];
if (src == null || src.length() == 0) continue; // 跳過空文件域
String fileName = uploadFileName[i];
// 安全處理:防止路徑遍歷、生成唯一文件名
String safeName = System.currentTimeMillis() + "_" + fileName.replaceAll("[^\\w\\.-]", "_");
File dest = new File(destDir, safeName);
try {
FileUtils.copyFile(src, dest); // Commons IO 高效複製
addActionMessage("文件 " + fileName + " 上傳成功,保存為:" + safeName);
} catch (IOException e) {
addActionError("文件 " + fileName + " 保存失敗:" + e.getMessage());
}
}
return SUCCESS;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<constant name="struts.devMode" value="true" />
<constant name="struts.i18n.encoding" value="UTF-8" />
<!-- 上傳大小限制:200MB -->
<constant name="struts.multipart.maxSize" value="209715200" />
<package name="upload" namespace="/" extends="struts-default">
<action name="upload" class="com.example.action.UploadAction">
<!-- 文件上傳攔截器配置 -->
<interceptor-ref name="fileUpload">
<param name="allowedTypes">
image/jpeg,image/png,image/gif,image/bmp,application/pdf
</param>
<param name="maximumSize">209715200</param> <!-- 200MB -->
</interceptor-ref>
<!-- 必須包含 defaultStack 以支持驗證、i18n 等核心功能 -->
<interceptor-ref name="defaultStack" />
<!-- 注入保存路徑 -->
<param name="savePath">/uploads</param>
<result name="success">/success.jsp</result>
<result name="input">/upload.jsp</result>
<result name="error">/error.jsp</result>
</action>
</package>
</struts>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<body>
<h3>上傳結果</h3>
<p>文件標題:<s:property value="title" /></p>
<s:if test="actionMessages.size() > 0">
<ul>
<s:iterator value="actionMessages">
<li><s:property /></li>
</s:iterator>
</ul>
</s:if>
<s:if test="actionErrors.size() > 0">
<ul style="color:red">
<s:iterator value="actionErrors">
<li><s:property /></li>
</s:iterator>
</ul>
</s:if>
<p><a href="upload.jsp">繼續上傳</a></p>
</body>
</html>
// Action 屬性改為 List
private List<File> upload = new ArrayList<>();
private List<String> uploadFileName = new ArrayList<>();
private List<String> uploadContentType = new ArrayList<>();
// Struts2 自動實例化並填充,用法與數組一致
<param name="allowedTypes">
image/jpeg,image/png,image/gif,image/bmp,
application/pdf,application/msword,
application/vnd.openxmlformats-officedocument.wordprocessingml.document
</param>
建議:同時驗證 uploadContentType 和文件擴展名,防止偽裝 MIME 類型。
/upload/chunk 接口接收分片,臨時目錄暫存/upload/merge 按序合併分片,校驗 MD5fileName = fileName.substring(fileName.lastIndexOf("/") + 1)chmod -R -x /uploads)struts.multipart.saveDir 指定專用臨時目錄,定時清理| 現象 | 原因 | 解決 |
|---|---|---|
| upload 為 null | form 缺少 enctype="multipart/form-data" | 檢查 JSP form 標籤屬性 |
| 上傳大文件報 400/500 | 超過 struts.multipart.maxSize 或 Tomcat maxPostSize | 調大兩處配置,Tomcat server.xml: maxPostSize="-1" |
| 文件名亂碼 | Tomcat 8+ 預設 ISO-8859-1 解析 URI | server.xml Connector 加 URIEncoding="UTF-8" |
| allowedTypes 不生效 | MIME 大小寫不匹配(如 Image/JPEG) | 統一轉小寫比對,或擴充白名單 |
Struts2 多文件上傳核心在於 同名 file 域 → 三個同名數組/List 自動綁定。配合 fileUpload 攔截器的類型、大小限制,以及 defaultStack 保障的核心功能,即可構建健壯的上傳功能。生產環境務必加上安全加固、異常處理、日誌審計,大文件場景請轉向分片上傳架構。