早期 ASP.NET 新聞系統多採動態頁面 (.aspx),每次請求皆觸發資料庫查詢與頁面生命週期,高流量下壓力巨大。本文實作一套批次生成靜態 HTML 的核心模組,採用「排程觸發 → 伺服器端渲染 → 靜態檔案落地」架構,將資料庫讀取與頁面生成集中在離線時段執行,上線環境僅提供靜態檔案,極大降低資料庫負載與回應延遲。
namespace makeHtmlfile
{
public class makeallfiles : System.Web.UI.Page
{
public string strcon; // 連線字串
public OleDbConnection conn; // 資料庫連線
public string strSQL; // 查詢 SQL
private void Page_Load(object sender, System.EventArgs e)
{
InitialPages(); // 進入點
}
public void InitialPages()
{
// 從 web.config 讀取 Access 資料庫路徑
strcon = "provider=Microsoft.jet.OLEDB.4.0;data Source="
+ Server.MapPath(ConfigurationSettings.AppSettings["MDBpath2"]) + ";";
strSQL = "select id, class1id, class2id from news order by id desc";
MakeAreaForShow(); // 1. 輸出客戶端下載容器
ReadNewsForWriteFileUserDataReader(); // 2. DataReader 模式生成 (預設)
// ReadNewsForWriteFileUserDataSet(); // 3. DataSet 模式生成 (備選)
}
// ...
}
}
利用 IE 專有 Download 行為 (Behavior) 實現伺服器推送腳本至客戶端執行:
public void MakeAreaForShow()
{
// 顯示進度/結果的容器
Response.Write("");
// IE Download Behavior 元件
Response.Write(" ");
}
限制: 僅支援 IE 瀏覽器。現代專案建議改用fetch/SignalR/WebSocket實現跨瀏覽器即時進度回報。
public void ReadNewsForWriteFileUserDataReader()
{
int num = 0;
string newsid, class1id, class2id;
OleDbDataReader dr = null;
using (OleDbConnection conn = new OleDbConnection(strcon))
{
conn.Open();
using (OleDbCommand cmd = new OleDbCommand(strSQL, conn))
{
dr = cmd.ExecuteReader();
while (dr.Read())
{
newsid = dr["id"].ToString();
class1id = dr["class1id"].ToString();
class2id = dr["class2id"].ToString();
WriteJScript(newsid, class1id, class2id); // 產生客戶端腳本
num++;
}
}
}
Response.Write(num.ToString()); // 輸出總筆數
}
優點: 僅保留單筆記錄在記憶體,適合大量資料 (十萬筆以上)。
public void ReadNewsForWriteFileUserDataSet()
{
DataSet ds = new DataSet();
int num = 0;
using (OleDbConnection conn = new OleDbConnection(strcon))
{
using (OleDbDataAdapter da = new OleDbDataAdapter(strSQL, conn))
{
da.Fill(ds, "news");
}
}
num = ds.Tables["news"].Rows.Count;
foreach (DataRow dr in ds.Tables["news"].Rows)
{
string newsid = dr["id"].ToString();
string class1id = dr["class1id"].ToString();
string class2id = dr["class2id"].ToString();
WriteJScript(newsid, class1id, class2id);
}
ds = null; // 釋放參考
Response.Write(num.ToString());
}
優點: 斷線操作、可重複遍歷、支援關聯篩選。缺點: 全量載入記憶體,大資料量易導致 OOM。
public void WriteJScript(string newsid, string class1id, string class2id)
{
Response.Write("");
}
流程說明:
<script> 區塊至瀏覽器。oDownload.startDownload 向 makefile2.aspx 發送參數化請求。makefile2.aspx 接收參數 → 查詢完整新聞內容 → 渲染 HTML → 寫入靜態檔案 → 回傳結果字串。onDownloadDone 回呼更新頁面顯示進度。| 舊架構痛點 | 現代替代方案 |
|---|---|
| IE 專用 Download Behavior | 前端 fetch + 後端 Web API + Server-Sent Events 或 SignalR 推送進度 |
| Response.Write 字串拼接 | Razor View / Template Engine (Scriban, Fluid) 渲染 HTML |
| Access (Jet OLEDB) | SQL Server / PostgreSQL / SQLite + Dapper/EF Core |
| 同步阻塞生成 | Hangfire / Quartz.NET 背景作業 + 進度持久化 |
| 實體靜態檔案 | CDN 快取 + 邊緣渲染 (Next.js ISR, Cloudflare Workers) |
管理員觸發生成
│
▼
makeallfiles.aspx (Page_Load)
│
├─► MakeAreaForShow() → 輸出 +
│
├─► ReadNewsForWriteFileUserDataReader()
│ │
│ ├─► 迴圈 dr.Read()
│ │ │
│ │ └─► WriteJScript(id, class1, class2) → 輸出
│ │
│ └─► 輸出總筆數
│
▼
瀏覽器接收大量 HTML+Script
│
├─► 依序觸發 oDownload.startDownload()
│ │
│ ▼
│ makefile2.aspx?id=xxx&class1id=yyy&class2id=zzz
│ │
│ ├─► 查詢新聞完整內容 (標題、內容、時間、作者...)
│ ├─► 套用 HTML 模板渲染
│ ├─► File.WriteAllText($"news_{id}.html", htmlContent)
│ └─► Response.Write("生成成功: news_xxx.html")
│
▼
onDownloadDone() 更新 showImport 顯示進度
│
▼
全數完成 → 靜態化部署完成
cmd.Parameters.AddWithValue) 替代字串拼接。Guid 或經過 Path.GetInvalidFileNameChars 過濾。Mutex/SemaphoreSlim) 避免重複觸發。此實作展示了 ASP.NET WebForms 時代「伺服器端推送腳本驅動客戶端批次下載」的經典模式。雖然技術棧已老舊,但串流式讀取 (DataReader)、離線快取 (DataSet)、客戶端非同步協作 的核心思想在現代雲原生架構中依然閃耀。重構時建議採用「背景作業佇列 + 靜態站點生成器 (SSG) + CDN」三位一體方案,徹底解耦生成與服務。