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

Struts2 文件上傳下載完整實現指南

     編輯:Java 相關知識

核心依賴

<dependency>
    <groupId>org.apache.struts</groupId>
    <artifactId>struts2-core</artifactId>
    <version>2.5.30</version>
</dependency>
<!-- 文件上傳必需 -->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.4</version>
</dependency>
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.11.0</version>
</dependency>

配置常量 (struts.xml / struts.properties)

<constant name="struts.multipart.maxSize" value="104857600" /> <!-- 100MB -->
<constant name="struts.multipart.saveDir" value="/tmp/struts-upload" /> <!-- 臨時目錄 -->
<constant name="struts.multipart.parser" value="jakarta" /> <!-- 解析器 -->

Action 實現:文件上傳

@Namespace("/file")
@Action(value = "upload", results = {
    @Result(name = "success", type = "json"),
    @Result(name = "input", location = "/upload.jsp")
})
public class FileUploadAction extends ActionSupport {

    private File upload;                    // 上傳文件
    private String uploadFileName;          // 原始文件名
    private String uploadContentType;       // MIME 類型
    private String savePath = "/var/www/uploads"; // 保存目錄

    // Struts2 自動注入:屬性名 = 表單 file 域名 + FileName/ContentType
    public void setUpload(File upload) { this.upload = upload; }
    public void setUploadFileName(String uploadFileName) { this.uploadFileName = uploadFileName; }
    public void setUploadContentType(String uploadContentType) { this.uploadContentType = uploadContentType; }

    @Override
    public String execute() {
        if (upload == null) {
            addFieldError("upload", "請選擇文件");
            return INPUT;
        }
        
        // 1. 校驗文件類型/大小
        if (!isAllowedType(uploadContentType)) {
            addFieldError("upload", "不支持的文件類型");
            return INPUT;
        }
        
        // 2. 生成唯一文件名防覆蓋
        String ext = FilenameUtils.getExtension(uploadFileName);
        String newName = UUID.randomUUID() + "." + ext;
        File dest = new File(savePath, newName);
        dest.getParentFile().mkdirs();
        
        // 3. 移動文件(Struts2 上傳文件在臨時目錄,需手動移動/複製)
        try {
            FileUtils.copyFile(upload, dest);
            // 或 Files.move(upload.toPath(), dest.toPath(), REPLACE_EXISTING);
        } catch (IOException e) {
            addActionError("保存失敗: " + e.getMessage());
            return ERROR;
        }
        
        // 4. 返回結果(JSON)
        Map result = Map.of(
            "name", newName,
            "originalName", uploadFileName,
            "size", upload.length(),
            "url", "/files/" + newName
        );
        ServletActionContext.getResponse().setContentType("application/json");
        try { ServletActionContext.getResponse().getWriter().write(new ObjectMapper().writeValueAsString(result)); } 
        catch (IOException ignored) {}
        return NONE; // 已手動輸出
    }
    
    private boolean isAllowedType(String contentType) {
        return contentType != null && 
               (contentType.startsWith("image/") || 
                contentType.equals("application/pdf") ||
                contentType.equals("application/msword"));
    }
}

JSP 上傳表單

<%@ taglib prefix="s" uri="/struts-tags" %>
<s:form action="file/upload" method="post" enctype="multipart/form-data">
    <s:file name="upload" label="選擇文件" accept="image/*,.pdf,.doc"/>
    <s:submit value="上傳" />
    <s:fielderror />
</s:form>

Action 實現:文件下載

@Action(value = "download", results = {
    @Result(name = "success", type = "stream", params = {
        "contentType", "application/octet-stream",
        "inputName", "inputStream",
        "contentDisposition", "attachment;filename=\"${downloadFileName}\"",
        "bufferSize", "4096"
    })
})
public class FileDownloadAction extends ActionSupport {

    private String fileName; // 請求參數
    private InputStream inputStream;
    private String downloadFileName; // 瀏覽器顯示的文件名

    public void setFileName(String fileName) { this.fileName = fileName; }
    public InputStream getInputStream() { return inputStream; }
    public String getDownloadFileName() { return downloadFileName; }

    @Override
    public String execute() {
        File file = new File("/var/www/uploads", fileName);
        if (!file.exists() || !file.isFile()) {
            addActionError("文件不存在");
            return ERROR;
        }
        
        try {
            inputStream = new FileInputStream(file);
            // 解決中文文件名亂碼(RFC 5987 編碼)
            downloadFileName = URLEncoder.encode(fileName, "UTF-8").replace("+", "%20");
        } catch (IOException e) {
            return ERROR;
        }
        return SUCCESS;
    }
}

struts.xml 結果類型配置對比

類型用途關鍵參數
dispatcher默認,轉發到 JSPlocation
redirect重定向location, parse=true
stream文件下載/二進制輸出inputName, contentType, contentDisposition, bufferSize
jsonJSON 響應root, contentType
freemarker/velocity模板引擎location

進階最佳實踐

  1. 多文件上傳:Action 定義 List uploads、List uploadFileNames
  2. 大文件斷點續傳:前端分片(WebUploader/Plupload),後端合併臨時分片文件
  3. 安全加固:
    • 文件類型雙重校驗(Content-Type + 魔數檢測)
    • 存儲目錄與 Web 根目錄隔離,通過 Stream 結果輸出
    • 文件名隨機化,防止路徑遍歷攻擊
  4. 雲存儲對接:上傳後直接流式傳輸到 OSS/S3,不落地本地磁盤
  5. 國際化文件名:下載時使用 Content-Disposition: attachment; filename*=UTF-8''encoded_name

常見問題排查

現象原因解決
upload 為 nullform 缺少 enctype="multipart/form-data"檢查 <s:form enctype="multipart/form-data">
上傳大文件失敗超過 struts.multipart.maxSize調大常量,同時檢查 Tomcat maxPostSize
中文文件名下載亂碼Content-Disposition 編碼錯誤使用 RFC 5987 格式或 URLEncoder.encode
臨時文件未清理Struts2 不自動刪除臨時文件定時任務清理 struts.multipart.saveDir

總結

Struts2 文件上傳下載核心在於:Action 屬性命名約定匹配表單域、Stream 結果類型實現下載、安全校驗不可省略。現代項目建議優先考慮 Spring MVC / Spring Boot + MultipartFile,配置更簡潔,功能更強大。

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