專案需求「匯出 Excel」看似簡單,實則因架構(Web/桌面)、資料量、樣式需求、部署環境不同,最佳方案大相逕庭。本文系統整理 **ASP.NET 五種主流方案** 與 **WinForms 兩種方案**,附完整可用代碼、優缺點比較表與選型決策樹,助你一分鐘鎖定正確實作。
將整個頁面(含按鈕、分頁、圖片)以 application/vnd.ms-excel 輸出。
Response.Clear();
Response.Buffer = true;
Response.ContentEncoding = System.Text.Encoding.UTF8;
Response.ContentType = "application/vnd.ms-excel";
Response.AddHeader("Content-Disposition", $"attachment;filename=Export_{DateTime.Now:yyyyMMdd}.xls");
this.EnableViewState = false;
// 頁面正常 Render,所有 HTML 皆進入 Excel
✅ 零代碼改動 ❌ 匯出垃圾標籤、樣式混亂、分頁器也進去
只匯出特定控制項的資料,解決方案 1 的雜訊問題。
public void ExportControl(Control ctrl, string fileName)
{
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.Buffer = true;
HttpContext.Current.Response.Charset = "UTF-8";
HttpContext.Current.Response.ContentEncoding = System.Text.Encoding.Default;
HttpContext.Current.Response.ContentType = "application/ms-excel";
HttpContext.Current.Response.AddHeader("Content-Disposition", $"attachment;filename={HttpUtility.UrlEncode(fileName)}.xls");
ctrl.Page.EnableViewState = false;
using (StringWriter sw = new StringWriter())
using (HtmlTextWriter hw = new HtmlTextWriter(sw))
{
ctrl.RenderControl(hw);
HttpContext.Current.Response.Write(sw.ToString());
}
HttpContext.Current.Response.End();
}
⚠️ 需在頁面覆寫 VerifyRenderingInServerForm;若控制項有分頁,僅匯出當頁。
直接遍歷 DataTable 產生 <table>,適合非 WebForms 或需自訂欄位順序/格式。
public void ExportDataTable(DataTable dt, string fileName)
{
HttpResponse resp = HttpContext.Current.Response;
resp.Clear();
resp.Buffer = true;
resp.Charset = "GB2312"; // 中文環境常用
resp.ContentEncoding = System.Text.Encoding.GetEncoding("GB2312");
resp.ContentType = "application/vnd.ms-excel";
resp.AddHeader("Content-Disposition", $"attachment;filename={HttpUtility.UrlEncode(fileName)}.xls");
// 標題列
for (int i = 0; i < dt.Columns.Count; i++)
resp.Write(dt.Columns[i].ColumnName + (i == dt.Columns.Count - 1 ? "\n" : "\t"));
// 資料列
foreach (DataRow row in dt.Rows)
{
for (int i = 0; i < dt.Columns.Count; i++)
resp.Write(row[i].ToString() + (i == dt.Columns.Count - 1 ? "\n" : "\t"));
}
resp.End();
}
✅ 完整資料、無分頁限制 ❌ 無樣式、大數據量字串拼接效能差
在伺服器啟動 Excel Application 寫入、格式化、存檔、再傳檔給客戶端。
// 關鍵片段(完整代碼極長,見原文)
Application excel = new ApplicationClass();
_Workbook wb = excel.Workbooks.Add(true);
_Worksheet ws = (_Worksheet)wb.ActiveSheet;
// ... 寫入標題、資料、設定格式、邊框、合併儲存格、自動欄寬 ...
wb.SaveCopyAs(serverPath);
excel.Quit();
// 釋放 COM 物件(關鍵!否則 Excel.exe 殘留)
Marshal.ReleaseComObject(ws); Marshal.ReleaseComObject(wb); Marshal.ReleaseComObject(excel);
GC.Collect();
// 傳檔下載
Response.WriteFile(serverPath);
✅ 真實 .xls、完整格式控制 ❌ **伺服器必裝 Office、COM 相容性地獄、效能極差、不支援高併發、微軟明確不建議伺服器端自動化 Office**
將 Excel 檔案產生到伺服器磁碟(或雲端儲存),再以 Response.WriteFile 或提供下載連結。
string path = Server.MapPath("~/Exports/Report_" + Guid.NewGuid() + ".csv");
using (FileStream fs = new FileStream(path, FileMode.Create))
using (StreamWriter sw = new StreamWriter(fs, Encoding.GetEncoding("GB2312")))
{
sw.WriteLine("編號,姓名,年齡");
foreach (DataRow dr in dt.Rows)
sw.WriteLine($"{dr["ID"]},{dr["Name"]},{dr["Age"]}");
}
Response.Clear();
Response.ContentType = "application/ms-excel";
Response.AddHeader("Content-Disposition", $"attachment;filename={HttpUtility.UrlEncode(Path.GetFileName(path))}");
Response.WriteFile(path);
Response.End();
✅ 可保留歷史檔案、支援大檔分段下載 ❌ 需管理磁碟空間、清理策略
string csvPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "Export_" + DateTime.Now.Ticks + ".csv");
using (StreamWriter sw = new StreamWriter(csvPath, false, Encoding.GetEncoding("GB2312")))
{
sw.WriteLine("自動編號,姓名,年齡");
foreach (DataRow dr in dt.Rows)
sw.WriteLine($"{dr["ID"]},{dr["Name"]},{dr["Age"]}");
}
Process.Start(csvPath); // 系統預設關聯 Excel 開啟
Excel.Application xlApp = new Excel.Application();
xlApp.Visible = false; xlApp.DisplayAlerts = false;
Excel.Workbook wb = xlApp.Workbooks.Add(Excel.XlWBATemplate.xlWBATWorksheet);
Excel.Worksheet ws = (Excel.Worksheet)wb.Worksheets[1];
// 寫入標題
for (int c = 0; c < dt.Columns.Count; c++)
{
ws.Cells[2, c + 1] = dt.Columns[c].ColumnName;
Excel.Range rng = (Excel.Range)ws.Cells[2, c + 1];
rng.Font.Bold = true; rng.Interior.ColorIndex = 15;
}
// 寫入資料
for (int r = 0; r < dt.Rows.Count; r++)
for (int c = 0; c < dt.Columns.Count; c++)
ws.Cells[r + 3, c + 1] = dt.Rows[r][c].ToString();
// 邊框、自動欄寬、存檔
Excel.Range all = ws.get_Range(ws.Cells[2,1], ws.Cells[dt.Rows.Count+2, dt.Columns.Count]);
all.Borders.LineStyle = Excel.XlLineStyle.xlContinuous;
all.Columns.AutoFit();
SaveFileDialog sfd = new SaveFileDialog { Filter = "Excel檔案|*.xlsx", DefaultExt = "xlsx" };
if (sfd.ShowDialog() == DialogResult.OK)
wb.SaveAs(sfd.FileName, Excel.XlFileFormat.xlOpenXMLWorkbook);
wb.Close(false); xlApp.Quit();
// 釋放 COM
Marshal.ReleaseComObject(ws); Marshal.ReleaseComObject(wb); Marshal.ReleaseComObject(xlApp);
GC.Collect();
HttpUtility.UrlEncode(fileName, Encoding.UTF8),否則 IE/Edge 下載檔名亂碼。HttpContext.Current.ApplicationInstance.CompleteRequest()。掌握這張地圖,下次接到「匯出 Excel」需求,你就能根據專案實況,在五分鐘內選定並實作最適合的方案。