DownloadAction 為 Struts 內建支援檔案下載的抽象類別。子類別只需實作 getStreamInfo() 回傳 StreamInfo(含 Content-Type 與 InputStream),框架自動處理下載回應。本文示範結合資料庫統計下載次數的完整範例。
// DownloadAction 內部介面
public static interface StreamInfo {
String getContentType();
InputStream getInputStream() throws IOException;
}
// 兩個便利實作
public FileStreamInfo(String contentType, File file); // 本地檔案
public ResourceStreamInfo(String contentType, ServletContext ctx, String path); // Web 根目錄下資源
t_dcountCREATE TABLE struts.t_dcount (
id INT NOT NULL PRIMARY KEY,
count INT NOT NULL,
filename VARCHAR(256) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=gbk;
id 使用檔名 hashCode() 作為主鍵。
package action;
import org.apache.struts.actions.DownloadAction;
import javax.servlet.http.*;
import java.io.*;
import java.sql.*;
public class FileDownloadAction extends DownloadAction {
private Connection conn;
private String basePath; // 來自 web.xml init-param
private String filename; // 來自請求參數 file
// 延遲建立資料庫連線
private void openConnection() throws Exception {
if (conn == null) {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(
"jdbc:mysql://localhost/struts?characterEncoding=GBK", "root", "1234");
}
}
// 查詢下載次數
private int getDownloadCount(int id) throws Exception {
openConnection();
PreparedStatement ps = conn.prepareStatement(
"SELECT count FROM t_dcount WHERE id = " + id);
ResultSet rs = ps.executeQuery();
return rs.next() ? rs.getInt(1) : 0;
}
// 遞增下載次數(不存在則插入)
private void incDownloadCount() throws Exception {
openConnection();
int id = filename.hashCode();
PreparedStatement ps = conn.prepareStatement(
"UPDATE t_dcount SET count = count + 1 WHERE id = " + id);
if (ps.executeUpdate() == 0) {
ps.executeUpdate(
"INSERT INTO t_dcount(id, count, filename) VALUES(" + id + ", 1, '" + filename + "')");
}
}
// 核心:提供下載串流資訊
protected StreamInfo getStreamInfo(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
FileInputStream fis = new FileInputStream(basePath + filename);
String contentType = "application/file";
// 關鍵:設定 Content-Disposition 讓瀏覽器顯示正確檔名
response.setHeader("content-disposition", "attachment; filename=" + filename);
incDownloadCount(); // 統計
return new StreamInfo() { // 匿名實作
public String getContentType() { return contentType; }
public InputStream getInputStream() throws IOException { return fis; }
};
}
// 無 file 參數時:列出目錄檔案與下載次數
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
basePath = getServlet().getInitParameter("downloadPath");
filename = request.getParameter("file");
if (filename == null) {
File dir = new File(basePath);
File[] files = dir.listFiles();
response.setCharacterEncoding("GBK");
PrintWriter out = response.getWriter();
out.println("");
for (File f : files) {
if (f.isFile() && !f.isHidden()) {
out.println("- " + f.getName()
+ " 下載次數:"
+ getDownloadCount(f.getName().hashCode()) + "
");
}
}
out.println("
");
return null;
} else {
return super.execute(mapping, form, request, response); // 觸發 getStreamInfo
}
}
}
<action path="/download" scope="request" type="action.FileDownloadAction" />
action Servlet 加入初始化參數:
<init-param>
<param-name>downloadPath</param-name>
<param-value>D:\download\</param-value> <!-- 必須以反斜線結尾 -->
</init-param>
struts-config.xml 設定 <controller maxFileSize="2M"/> 限制上傳大小(不影響下載)。訪問 https://localhost:8080/samples/download.do 列出檔案清單,點選連結下載並自動統計次數。
DownloadAction 封裝了下載回應的標準流程,開發者專注於提供 InputStream 與 Content-Type,並能輕鬆插入統計、權限檢查等業務邏輯。注意設定 content-disposition 標頭確保瀏覽器正確顯示下載檔名。