在 Web 應用中實現「點擊鏈接下載服務器生成的 Excel 文件」是常見需求。本文基於 JBuilder 2005 向導,完整演示從創建 HttpServlet、配置 web.xml、設置響應頭、流式輸出文件、到 JSP 頁面集成的全流程。
ExcelFileServlet,包:bookstore.servletyear (String)、month (String)/excelfileservletpackage bookstore.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
public class ExcelFileServlet extends HttpServlet {
public void init() throws ServletException {}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String year = request.getParameter("year");
if (year == null) year = "2005";
String month = request.getParameter("month");
if (month == null) month = "1";
PrintWriter out = response.getWriter(); // 後續將替換
// @todo implement GET
}
public void destroy() {}
}
<servlet>
<servlet-name>excelfileservlet</servlet-name>
<servlet-class>bookstore.servlet.ExcelFileServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>excelfileservlet</servlet-name>
<url-pattern>/excelfileservlet</url-pattern>
</servlet-mapping>
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 1. 解析並驗證參數
int year, month;
try { year = Integer.parseInt(request.getParameter("year")); }
catch (NumberFormatException e) { year = 2005; }
try { month = Integer.parseInt(request.getParameter("month")); }
catch (NumberFormatException e) { month = 1; }
// 2. 構建文件路徑
String fileName = "log_" + year + "_" + month + ".xls";
File file = new File("D:/serverLog/" + fileName); // 注意路徑分隔符
// 3. 關鍵:設置響應頭觸發瀏覽器下載對話框
response.setContentType("application/x-msdownload"); // 或 application/octet-stream
response.setContentLength((int) file.length());
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
// 4. 流式輸出(避免大文件 OOM)
try (FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
OutputStream out = response.getOutputStream()) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = bis.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
out.flush();
}
// 無需 catch,Servlet 容器會處理 IOException
}
| 技術點 | 作用 | 注意事項 |
|---|---|---|
Content-Type: application/x-msdownload | 告知瀏覽器「此內容需下載」,而非頁面渲染 | 也可用 application/octet-stream、application/vnd.ms-excel |
Content-Disposition: attachment; filename=... | 指定下載默認文件名,觸發「另存為」對話框 | 文件名含特殊字符需 URL 編碼 (RFC 5987) |
Content-Length | 讓瀏覽器顯示進度條、支持斷點續傳 | 文件過大超過 Integer.MAX_VALUE 需分塊傳輸 |
BufferedInputStream + 1KB 緩衝 | 減少系統調用、平衡內存佔用 | 大文件可調大至 8KB-64KB |
try-with-resources (Java 7+) | 自動關閉流、防洩漏 | JBuilder 2005 可能需手動 finally close() |
<%@ page contentType="text/html; charset=GBK" %>
<%@ taglib uri="https://jakarta.apache.org/taglibs/datetime-1.0" prefix="dt" %>
...
當前時間:<dt:format pattern="MM/dd/yyyy hh:mm"><dt:currentTime/></dt:format>
<!-- 下載鏈接:帶參數指向 Servlet -->
<a href="/webModule/excelfileservlet?year=2005&month=1">
下載系統啟動日誌 (2025年1月)
</a>
<br>
<a href="quit.jsp">點擊這裡退出系統</a>
Range 請求頭,實現 206 Partial Content../../etc/passwd)filename*=UTF-8''%E6%97%A5%E8%AA%8C.xlsPrintWriter 輸出二進制 → 字符編碼破壞文件 → 改用 OutputStreamContent-Length → 瀏覽器無進度條、可能下載不完整ServletContext.getRealPath() 或配置屬性readAllBytes() → OOM → 必須分塊流式輸出掌握此模式,文件下載功能「一次寫對,到處可用」。