採用「模板檔 + 資料庫 + 靜態化產生器」架構:
{Title}、{Content}、{Pager} 等置換標記。<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>{Title}</title>
</head>
<body>
<div align="center"><h1>{Title}</h1></div>
<div>瀏覽 <font color="red"><script src="/ClickCount.aspx?NewsId={NewsId}"></script></font> 次 時間:{Time}</div>
<div>{Content}</div>
<div>{Pager}</div>
<!-- 評論表單區塊 -->
<form action="/AddComment.aspx" method="post">
<input type="hidden" name="NewsId" value="{NewsId}" />
<textarea name="CommentContent"></textarea>
<input type="submit" value="發表評論" />
</form>
</body>
</html>
置換標記:{Title}、{NewsId}、{Time}、{Content}、{Pager}。
<%@ Page Language="C#" ValidateRequest="false" CodeFile="NewsAdd.aspx.cs" Inherits="NewsAdd" %>
<%@ Register TagPrefix="FCK" Namespace="FredCK.FCKeditorV2" Assembly="FredCK.FCKeditorV2" %>
<html><body>
<form id="form1" runat="server">
標題:<asp:TextBox ID="txtTitle" runat="server" Width="400px" /><br />
內容:<FCK:FCKeditor ID="txtContent" runat="server" Height="400px" Width="70%" BasePath="~/FCKeditor/" /><br />
<asp:Button ID="btnPublish" runat="server" Text="發布靜態頁" OnClick="btnPublish_Click" />
<asp:Label ID="lblMsg" runat="server" ForeColor="Green" />
</form>
</body></html>
using System;
using System.Data;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.UI;
public partial class NewsAdd : Page
{
protected void btnPublish_Click(object sender, EventArgs e)
{
string title = txtTitle.Text.Trim();
string rawContent = txtContent.Value.Trim(); // FCKeditor 內容
if (string.IsNullOrEmpty(title) || string.IsNullOrEmpty(rawContent))
{
lblMsg.Text = "標題與內容不可為空";
return;
}
// 1. 存入資料庫取得 NewsId
string fileNameBase = DateTime.Now.ToString("yyMMddHHmmss");
string fileName = fileNameBase + ".shtml";
string connStr = System.Configuration.ConfigurationManager.ConnectionStrings["connStr"].ConnectionString;
int newsId = 0;
using (SqlConnection conn = new SqlConnection(connStr))
{
string sql = "INSERT INTO News (Title, Content, FileName) VALUES (@Title, @Content, @FileName); SELECT SCOPE_IDENTITY()";
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
cmd.Parameters.AddWithValue("@Title", title);
cmd.Parameters.AddWithValue("@Content", rawContent);
cmd.Parameters.AddWithValue("@FileName", fileName);
conn.Open();
newsId = Convert.ToInt32(cmd.ExecuteScalar());
}
}
// 2. 依字數切割分頁 (每頁約 2000 字)
int pageSize = 2000;
string[] pages = SplitContentByLength(rawContent, pageSize);
int totalPages = pages.Length;
// 3. 讀取模板
string templatePath = Server.MapPath("~/Templates/template.html");
string template = File.ReadAllText(templatePath, Encoding.GetEncoding("gb2312"));
// 4. 逐頁產生靜態檔
string outputDir = Server.MapPath($"~/NewsFiles/{DateTime.Now:yyMMdd}/");
if (!Directory.Exists(outputDir)) Directory.CreateDirectory(outputDir);
for (int i = 0; i < totalPages; i++)
{
string pageFile = (i == 0) ? fileNameBase + ".shtml" : $"{fileNameBase}_{i}.shtml";
string pagePath = Path.Combine(outputDir, pageFile);
string html = template
.Replace("{Title}", title)
.Replace("{NewsId}", newsId.ToString())
.Replace("{Time}", DateTime.Now.ToString("yyyy/MM/dd"))
.Replace("{Content}", pages[i])
.Replace("{Pager}", BuildPagerHtml(fileNameBase, i, totalPages));
File.WriteAllText(pagePath, html, Encoding.GetEncoding("gb2312"));
}
lblMsg.Text = $"發布成功!共產生 {totalPages} 頁靜態檔案。";
}
/// 依指定長度切割內容,盡量在標點符號處斷行
private string[] SplitContentByLength(string content, int maxLen)
{
List result = new List();
while (content.Length > maxLen)
{
int cut = content.LastIndexOfAny(new[] { '。', '?', '!', '\n' }, maxLen);
if (cut <= 0) cut = maxLen;
result.Add(content.Substring(0, cut + 1));
content = content.Substring(cut + 1);
}
if (content.Length > 0) result.Add(content);
return result.ToArray();
}
/// 產生分頁導覽 HTML
private string BuildPagerHtml(string baseName, int currentIndex, int totalPages)
{
if (totalPages <= 1) return string.Empty;
StringBuilder sb = new StringBuilder();
sb.Append("");
// 上一頁
if (currentIndex > 0)
{
string prevFile = (currentIndex == 1) ? baseName + ".shtml" : $"{baseName}_{currentIndex - 1}.shtml";
sb.Append($"上一頁 ");
}
// 頁碼
for (int p = 1; p <= totalPages; p++)
{
string pageFile = (p == 1) ? baseName + ".shtml" : $"{baseName}_{p - 1}.shtml";
if (p == currentIndex + 1)
sb.Append($"{p} ");
else
sb.Append($"{p} ");
}
// 下一頁
if (currentIndex < totalPages - 1)
{
string nextFile = $"{baseName}_{currentIndex + 1}.shtml";
sb.Append($"下一頁");
}
sb.Append("");
return sb.ToString();
}
}
NewsId 供點擊統計、評論關聯。SplitContentByLength 優先在句號、換行處斷行,避免截斷句子。NewsFiles/yyMMdd/ 避免單一目錄檔案過多影響檔案系統效能。gb2312,符合中文舊系統相容需求;新專案建議全面改用 UTF-8。