Struts2 內建強大的文件上傳支援,基於 FileUploadInterceptor 自動將表單文件封裝為 File 物件。本文以圖片上傳為例,涵蓋單/多文件、類型大小限制、自定義儲存路徑、安全防護與完整配置實戰。
表單 <s:file name="myFile"> 提交時,Struts2 自動注入 Action 三個屬性(setXxx 方法):
| 屬性 | 類型 | 說明 |
|---|---|---|
| myFile | File | 上傳文件臨時存放物件 |
| myFileContentType | String | MIME 類型,如 image/jpeg |
| myFileFileName | String | 原始文件名 |
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head><title>圖片上傳</title></head>
<body>
<s:form action="upload" method="post" enctype="multipart/form-data">
<s:file name="myFile" label="選擇圖片" accept="image/*"/>
<s:textfield name="description" label="圖片描述"/>
<s:submit value="上傳"/>
</s:form>
</body>
</html>
package com.example.action;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.ServletActionContext;
import java.io.*;
import java.util.Date;
public class UploadAction extends ActionSupport {
// 自動注入屬性
private File myFile;
private String myFileContentType;
private String myFileFileName;
private String description;
// 回顯用
private String savedFileName;
private String message;
// 配置常數
private static final long MAX_SIZE = 5 * 1024 * 1024; // 5MB
private static final String[] ALLOWED_TYPES = {"image/jpeg", "image/png", "image/gif"};
private static final String UPLOAD_DIR = "uploads";
@Override
public String execute() {
// 1. 基礎校驗
if (myFile == null) {
message = "請選擇文件";
return INPUT;
}
if (myFile.length() > MAX_SIZE) {
message = "文件大小超過 5MB 限制";
return INPUT;
}
if (!isAllowedType(myFileContentType)) {
message = "僅支援 JPG/PNG/GIF 格式";
return INPUT;
}
// 2. 生成唯一文件名:時間戳 + 隨機數 + 原副檔名
String ext = getExtension(myFileFileName);
savedFileName = new Date().getTime() + "_" +
(int)(Math.random() * 10000) + "." + ext;
// 3. 獲取伺服器絕對儲存路徑
String realPath = ServletActionContext.getServletContext()
.getRealPath("/" + UPLOAD_DIR);
File destDir = new File(realPath);
if (!destDir.exists()) destDir.mkdirs();
// 4. 文件複製(緩衝流,避免大文件記憶體溢出)
try (InputStream in = new BufferedInputStream(new FileInputStream(myFile));
OutputStream out = new BufferedOutputStream(
new FileOutputStream(new File(destDir, savedFileName)))) {
byte[] buffer = new byte[8192];
int len;
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
message = "上傳成功!";
return SUCCESS;
} catch (IOException e) {
message = "上傳失敗:" + e.getMessage();
e.printStackTrace();
return ERROR;
}
}
private boolean isAllowedType(String contentType) {
for (String t : ALLOWED_TYPES) if (t.equalsIgnoreCase(contentType)) return true;
return false;
}
private String getExtension(String fileName) {
int idx = fileName.lastIndexOf('.');
return (idx > 0) ? fileName.substring(idx + 1).toLowerCase() : "bin";
}
// --- Getter/Setter ---
public void setMyFile(File myFile) { this.myFile = myFile; }
public void setMyFileContentType(String myFileContentType) { this.myFileContentType = myFileContentType; }
public void setMyFileFileName(String myFileFileName) { this.myFileFileName = myFileFileName; }
public void setDescription(String description) { this.description = description; }
public String getSavedFileName() { return savedFileName; }
public String getMessage() { return message; }
public String getDescription() { return description; }
}
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<body>
<h3><s:property value="message"/></h3>
<s:if test="savedFileName != null">
<div>描述:<s:property value="description"/></div>
<img src="<s:url value="/uploads/%{savedFileName}"/>"
alt="上傳圖片" style="max-width:400px;"/>
</s:if>
<a href="upload.jsp">繼續上傳</a>
</body>
</html>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<package name="upload" namespace="/" extends="struts-default">
<action name="upload" class="com.example.action.UploadAction">
<!-- 文件上傳攔截器堆疊,含大小/類型限制 -->
<interceptor-ref name="fileUpload">
<param name="maximumSize">5242880</param> <!-- 5MB -->
<param name="allowedTypes">image/jpeg,image/png,image/gif</param>
</interceptor-ref>
<interceptor-ref name="defaultStack"/>
<result name="success">/result.jsp</result>
<result name="input">/upload.jsp</result>
<result name="error">/error.jsp</result>
</action>
</package>
</struts>
<s:file name="files" label="圖片1"/>
<s:file name="files" label="圖片2"/>
<s:file name="files" label="圖片3"/>
private List files = new ArrayList<>();
private List filesContentType = new ArrayList<>();
private List filesFileName = new ArrayList<>();
public void setFiles(List files) { this.files = files; }
public void setFilesContentType(List filesContentType) { this.filesContentType = filesContentType; }
public void setFilesFileName(List filesFileName) { this.filesFileName = filesFileName; }
@Override
public String execute() {
for (int i = 0; i < files.size(); i++) {
File f = files.get(i);
String type = filesContentType.get(i);
String name = filesFileName.get(i);
// 單文件處理邏輯...
}
return SUCCESS;
}
maxPostSize + 前端 JS 預檢| 現象 | 原因 | 解決 |
|---|---|---|
| myFile 為 null | 表單缺少 enctype="multipart/form-data" | 檢查 <s:form> 屬性 |
| 中文文件名亂碼 | Tomcat 8+ 預設 UTF-8,舊版需 URIEncoding | server.xml 設 URIEncoding="UTF-8" |
| 上傳大文件失敗 | 超過 maximumSize 或 Tomcat post 大小限制 | 調整 struts.xml & server.xml maxPostSize |
| 權限拒絕 | 伺服器目錄無寫入權限 | chmod 755 uploads 目錄 |
Struts2 文件上傳核心在於「三屬性約定」與「fileUpload 攔截器堆疊」。生產環境務必實施:類型雙重驗證、路徑隔離、文件名隨機化、大小多層限制、病毒掃描。配合 Nginx 反向代理靜態資源服務,可構建安全高效的上傳服務。