傳統 `Microsoft.Office.Interop.Excel` 需在伺服器安裝 Office、處理 COM 權限、進程殘留導致記憶體洩漏,且無法支援高併發。NPOI 是 Apache POI 的 .NET 移植版,純 managed code、無需安裝 Office、效能極佳、支援 .xls (HSSF) 與 .xlsx (XSSF)。本文示範「載入模板 → 填入資料 → 重新計算公式 → 串流輸出下載」的標準流程。
| 傳統 Interop 問題 | NPOI 優勢 |
|---|---|
| 需安裝 Office、修補漏洞 | 純 .NET DLL,零依賴 |
| COM 權限設定複雜 (DCOMCNFG) | 無權限問題,中信任環境可用 |
| 每次導出產生 EXCEL.EXE 進程,不自動釋放 | 無進程,記憶體可控,支援高併發 |
| 自動轉換數字格式 (000123 → 123) | 可強制設定 CellType = String 保留前導零 |
| 公式/前 8 列型別推測導致科學記號 | 完全由程式控制格式,無驚喜 |
using NPOI.HSSF.UserModel; // .xls
using NPOI.SS.UserModel; // 介面層,支援 .xls/.xlsx 通用
using System.IO;
using System.Web;
public void ExportExcel()
{
// 1. 載入模板到工作簿
HSSFWorkbook workbook;
string templatePath = HttpContext.Current.Request.PhysicalApplicationPath + @"Template\Book1.xls";
using (FileStream fs = new FileStream(templatePath, FileMode.Open, FileAccess.Read))
{
workbook = new HSSFWorkbook(fs); // 自動解析模板樣式、公式、合併儲存格
}
// 2. 取得工作表並填入資料
ISheet sheet = workbook.GetSheet("Sheet1"); // 或 workbook.GetSheetAt(0)
// 模板第 2~13 列 (索引 1~12) 已預設格式,直接寫入數值
sheet.GetRow(1).GetCell(1).SetCellValue(200200);
sheet.GetRow(2).GetCell(1).SetCellValue(300);
sheet.GetRow(3).GetCell(1).SetCellValue(500050);
sheet.GetRow(4).GetCell(1).SetCellValue(8000);
sheet.GetRow(5).GetCell(1).SetCellValue(110);
sheet.GetRow(6).GetCell(1).SetCellValue(100);
sheet.GetRow(7).GetCell(1).SetCellValue(200);
sheet.GetRow(8).GetCell(1).SetCellValue(210);
sheet.GetRow(9).GetCell(1).SetCellValue(2300);
sheet.GetRow(10).GetCell(1).SetCellValue(240);
sheet.GetRow(11).GetCell(1).SetCellValue(180123);
sheet.GetRow(12).GetCell(1).SetCellValue(150);
// 3. 強制重新計算所有公式 (模板若含 SUM/AVERAGE 等)
sheet.ForceFormulaRecalculation = true;
// 4. 設定回應標頭:觸發瀏覽器下載
HttpResponse resp = HttpContext.Current.Response;
resp.ContentType = "application/vnd.ms-excel"; // .xls MIME
resp.AddHeader("Content-Disposition", "attachment;filename=ExportReport.xls");
resp.Clear(); // 清除緩衝區 HTML,避免混雜
// 5. 寫入 MemoryStream 並輸出二進位
using (MemoryStream ms = new MemoryStream())
{
workbook.Write(ms);
resp.BinaryWrite(ms.ToArray());
resp.End(); // 結束回應
}
}
| 步驟 | 關鍵 API | 注意事項 |
|---|---|---|
| 載入模板 | `new HSSFWorkbook(fs)` | FileStream 需 `FileAccess.Read`,用完即 Dispose,避免鎖定檔案 |
| 存取儲存格 | `sheet.GetRow(i).GetCell(j)` | 若儲存格不存在會回傳 null,建議封裝安全取值擴充方法 |
| 寫入數值 | `cell.SetCellValue(val)` | 多載支援 string/double/bool/DateTime,自動對應 CellType |
| 保留前導零 | `cell.SetCellValue("000123"); cell.CellType = CellType.String;` | 強制文字格式,防止自動轉數值 |
| 日期格式 | `cell.SetCellValue(DateTime.Now); cell.CellStyle.DataFormat = HSSFDataFormat.GetBuiltinFormat("yyyy/mm/dd");` | 需建立/重用 CellStyle |
| 公式重算 | `sheet.ForceFormulaRecalculation = true` | 僅支援 NPOI 內建函數,複雜 VBA/外部加載函數不支援 |
| 串流輸出 | `workbook.Write(ms)` → `Response.BinaryWrite` | 避免先存檔再傳輸,減少 I/O 與暫存檔清理 |
// 依副檔名自動選擇
string ext = Path.GetExtension(templatePath).ToLower();
IWorkbook workbook = ext switch
{
".xls" => new HSSFWorkbook(fs),
".xlsx" => new XSSFWorkbook(fs),
_ => throw new NotSupportedException("僅支援 .xls/.xlsx")
};
// 後續操作皆使用 IWorkbook/ISheet/ICell 介面,程式碼不變
NPOI 是 ASP.NET 導出 Excel 的最佳選擇:零依賴、高效能、完整支援樣式/公式/合併儲存格。掌握「模板法」—— 先在 Excel 設計好版面與格式,程式只負責填資料、觸發公式重算、串流輸出 —— 可大幅降低維護成本,並完美解決 Interop 所有痛點。