專案常需「資料→Excel」與「Excel→資料庫」雙向操作,本文總結十大主流方案優缺點、適用場景、核心代碼片段,協助技術選型。
| 方案 | 優點 | 缺點 | 適用場景 | 相依性 |
|---|---|---|---|---|
| NPOI | 免費開源、無需 Office、支援 .xls/.xlsx/.docx、功能完整 (樣式/公式/圖表)、串流式讀寫 | 大檔案記憶體佔用高、API 複雜 | 伺服器端匯入匯出、跨平台、無 Office 環境 | NPOI.dll + SharpZipLib |
| COM Interop | 完整 Excel 物件模型、支援所有 UI 操作 (宏/圖表/樞紐分析) | 需安裝 Office、效能極差、佔用高、伺服器不穩定、版本相容性差 | 桌面端自動化、需複雜 UI 操作、舊系統維護 | Microsoft.Office.Interop.Excel |
| OleDb (ACE/Jet) | SQL 語法查詢/寫入、無需 Office、支援 .xls/.xlsx | 型別推斷易錯 (首 8 列決定)、無法操作樣式/公式、64 位元需 ACE Provider | 簡單表格資料匯入匯出、類資料庫操作 | System.Data.OleDb + ACE Provider |
| XML (Excel 2003 XML) | 純文字、易產生/解析、無相依 | 僅支援舊版 XML 格式、無樣式/公式、大檔案效能差 | 極簡匯出、舊系統相容 | System.Xml |
| js 前端匯出 (DataTables/TableTools) | 零後端壓力、用戶端直接下載、支援多格式 | 大量資料瀏覽器卡頓、受限於記憶體、無法處理敏感資料 | 前端導出、報表下載、資料量 < 1 萬列 | DataTables.js + Buttons.js |
| CSV | 極簡、通用、無相依、串流處理、支援超大檔 | 無樣式/多工作表/公式、編碼易亂碼、無型別 | 資料交換、ETL 中繼、日誌匯出、大數據 | System.IO |
| Aspose.Cells | 功能最強 (渲染/轉 PDF/圖表/樞紐)、無需 Office、效能佳 | 商業授權昂貴、體積大 | 企業級報表、複雜樣式、PDF 轉換、預算充足 | Aspose.Cells.dll |
| OpenXML SDK | 微軟官方、底層控制最細、無需 Office、支援 .xlsx/.docx/.pptx | 學習曲線極陡、代碼冗長、需懂 OOXML 結構 | 深度定制、高效能串流、文件結構操作 | DocumentFormat.OpenXml.dll |
| EPPlus | API 友善、支援 .xlsx、效能好、功能豐富 (樣式/公式/圖表/資料驗證) | v5+ 改為商業授權 (Polyform Noncommercial)、v4 免費但無維護 | .xlsx 現代專案、團隊熟悉 EPPlus API | EPPlus.dll |
| 模板填充 | 設計與代碼分離、美工維護樣板、代碼只填資料 | 需額外模板引擎 (NPOI/EPPlus/OpenXML 皆可) | 固定格式報表、複雜排版、非技術人員維護樣式 | 視底層庫而定 |
public static byte[] ExportDataTable(DataTable dt, string sheetName)
{
using (var workbook = new XSSFWorkbook())
{
var sheet = workbook.CreateSheet(sheetName);
// 標題列
var headerRow = sheet.CreateRow(0);
for (int c = 0; c < dt.Columns.Count; c++)
headerRow.CreateCell(c).SetCellValue(dt.Columns[c].ColumnName);
// 資料列
for (int r = 0; r < dt.Rows.Count; r++)
{
var row = sheet.CreateRow(r + 1);
for (int c = 0; c < dt.Columns.Count; c++)
row.CreateCell(c).SetCellValue(dt.Rows[r][c]?.ToString() ?? "");
}
using (var ms = new MemoryStream())
{
workbook.Write(ms);
return ms.ToArray();
}
}
}
public static DataTable ImportExcel(string filePath, string sheetName)
{
string connStr = $"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={filePath};Extended Properties='Excel 12.0;HDR=YES;IMEX=1'";
using (var conn = new OleDbConnection(connStr))
{
conn.Open();
using (var cmd = new OleDbCommand($"SELECT * FROM [{sheetName}$]", conn))
using (var adapter = new OleDbDataAdapter(cmd))
{
var dt = new DataTable();
adapter.Fill(dt);
return dt;
}
}
}
public static void ExportCsv(DataTable dt, Stream output, Encoding encoding = null)
{
encoding = encoding ?? Encoding.UTF8;
using (var writer = new StreamWriter(output, encoding))
{
// 標題
writer.WriteLine(string.Join(",", dt.Columns.Cast().Select(c => EscapeCsv(c.ColumnName))));
// 資料
foreach (DataRow row in dt.Rows)
{
writer.WriteLine(string.Join(",", row.ItemArray.Select(v => EscapeCsv(v?.ToString() ?? ""))));
}
}
}
static string EscapeCsv(string s) => s.Contains(",") || s.Contains("\"") || s.Contains("\n")
? "\"" + s.Replace("\"", "\"\"") + "\"" : s;
// 模板檔案預留 {Name}、{Date} 等標記或具名範圍
using (var pkg = new ExcelPackage(new FileInfo("Template.xlsx")))
{
var ws = pkg.Workbook.Worksheets[0];
ws.Cells["NamedRange_Name"].Value = "John Doe";
ws.Cells["NamedRange_Date"].Value = DateTime.Today;
pkg.SaveAs(new FileInfo("Output.xlsx"));
}
沒有完美方案,只有最適合當前約束的方案。**NPOI 綜合性價比最高**,建議作為預設選擇,特定場景再引入專用庫。