Struts 1 內置 org.apache.struts.actions.DownloadAction 提供了基礎文件下載能力,但默認使用服務端物理文件名。本文演示如何通過繼承 DownloadAction 並重寫關鍵方法,實現下載時動態重命名文件(如:用戶看到「我的報表.pdf」而非「report_20231001_001.pdf」)。
DownloadActiongetStreamInfo():指定文件流來源(MIME 類型、路徑)execute():在父類執行前,通過 response.setHeader("Content-Disposition", ...) 設置自定義下載文件名package com.fenghuo.struts.download;
import java.net.URLEncoder;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.actions.DownloadAction;
import com.fenghuo.music.util.FileUtils;
public class DownLoad extends DownloadAction {
@Override
protected StreamInfo getStreamInfo(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws Exception {
// 1. 獲取請求參數:資源標識
String resname = request.getParameter("resname");
// 2. 返回資源流信息:MIME類型、ServletContext、相對路徑
return new DownloadAction.ResourceStreamInfo(
"audio/x-mpeg", // MIME 類型
this.getServlet().getServletContext(), // ServletContext
"/resource/music/" + resname // 相對路徑
);
}
@Override
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws Exception {
// 1. 獲取參數
String resname = request.getParameter("resname");
// 2. 查詢真實文件名(業務邏輯:從 Map/DB 獲取)
FileUtils fu = new FileUtils(this.getServlet().getServletContext());
Map<String, String> musmap = fu.getMusmap();
String realFileName = musmap.get(resname); // 如:周杰倫 - 稻香.mp3
// 3. 設置 Content-Disposition Header,指定下載文件名
// URLEncoder 編碼防止中文亂碼
String encodedName = URLEncoder.encode(realFileName, "UTF-8");
response.setHeader("Content-Disposition", "attachment;filename=" + encodedName);
// 4. 調用父類 execute 執行實際下載邏輯
return super.execute(mapping, form, request, response);
}
}
contentType、servletContext、path(相對於 WebApp 根目錄)servletContext.getResourceAsStream(path) 獲取輸入流attachment; filename="encoded_name.ext"
attachment:強制下載彈框,而非瀏覽器直接打開filename:用戶看到的下載文件名,**必須 URL 編碼**URLEncoder.encode(name, "UTF-8"),空格會變 +,瀏覽器兼容性良好必須在 super.execute() 之前設置 Header,因為父類會直接寫入響應體並提交響應,之後無法修改 Header。
<action path="/download"
type="com.fenghuo.struts.download.DownLoad"
name="downloadForm"
scope="request">
</action>
<a href="/app/download.do?resname=report_20231001_001.pdf">下載報表</a>
| 問題 | 原因 | 解決 |
|---|---|---|
| 中文文件名亂碼 | 未 URL 編碼 / 編碼不一致 | 統一用 URLEncoder.encode(name, "UTF-8"),前端頁面編碼 UTF-8 |
| Firefox 仍亂碼 | 瀏覽器對 RFC 5987 支持差異 | 嘗試 filename*=UTF-8''encoded 格式,或用 Content-Disposition: attachment; filename="fallback.txt"; filename*=UTF-8''real.txt |
| 大文件下載中斷 | 無 Content-Length / 連接超時 | 在 getStreamInfo 返回自定義 StreamInfo 實現 getContentLength(),或配置 Tomcat connectionTimeout |
| 權限控制 | 直接暴露文件路徑 | execute() 中校驗用戶權限,無權返回錯誤 Forward |
Struts 1 文件下載重命名核心在於:繼承 DownloadAction → 重寫 getStreamInfo 定位文件 → execute() 前設置 Content-Disposition Header → 父類執行流傳輸。此模式可靈活支持:數據庫存儲文件元數據、權限校驗、動態文件名生成、多語言文件名等企業級場景。