在企業級應用中,Excel 仍是資料交換最通用的格式。Aspose.Cells 作為無需安裝 Office、支援 .xls/.xlsx/.csv/.ods 全格式的純 .NET 元件,是伺服器端匯入匯出的首選。本文聚焦「讀取資料並轉為 DataTable/DataSet」的高頻場景,提供生產可用的最佳實踐。
// NuGet 安裝
Install-Package Aspose.Cells
// 命名空間
using Aspose.Cells;
using System.Data;
| 類別 | 職責 |
|---|---|
Workbook | 代表整個 Excel 檔案,入口點 |
Worksheet | 單一工作表 |
Cells | 儲存格集合,提供索引器、區域操作、匯出功能 |
Cell | 單一儲存格,含 Value, StringValue, Type, Style |
public IEnumerable<Dictionary<string, object>> ReadExcelRowByRow(string filePath, int sheetIndex = 0, bool hasHeader = true)
{
var workbook = new Workbook(filePath);
var sheet = workbook.Worksheets[sheetIndex];
var cells = sheet.Cells;
int startRow = hasHeader ? cells.FirstRow + 1 : cells.FirstRow;
int endRow = cells.MaxDataRow;
int startCol = cells.FirstColumn;
int endCol = cells.MaxDataColumn;
// 取得標題列
var headers = new List<string>();
if (hasHeader)
{
for (int c = startCol; c <= endCol; c++)
headers.Add(cells[cells.FirstRow, c].StringValue.Trim());
}
for (int r = startRow; r <= endRow; r++)
{
var rowDict = new Dictionary<string, object>();
bool allNull = true;
for (int c = startCol; c <= endCol; c++)
{
var cell = cells[r, c];
object val = cell.Type == CellValueType.IsNull ? null : cell.Value;
string key = hasHeader && c < headers.Count ? headers[c] : $"Col{c}";
rowDict[key] = val;
if (val != null) allNull = false;
}
if (!allNull) yield return rowDict; // 跳過全空列
}
}
public DataTable ExportToDataTable(string filePath, int sheetIndex = 0, bool firstRowAsHeader = true)
{
var workbook = new Workbook(filePath);
var cells = workbook.Worksheets[sheetIndex].Cells;
// ExportDataTable 參數:startRow, startCol, totalRows, totalCols, exportColumnName
// firstRowAsHeader=true 時:第 0 列當欄位名,資料從第 1 列開始
// firstRowAsHeader=false 時:第 0 列也是資料,欄位名自動產生 Column0, Column1...
int totalRows = cells.MaxDataRow + 1; // MaxDataRow 是 0-based 索引
int totalCols = cells.MaxDataColumn + 1;
var options = new ExportTableOptions
{
ExportColumnName = firstRowAsHeader,
CheckMergedCells = true, // 合併儲存格自動填充
PlotVisibleRowsOnly = false, // 包含隱藏列
ExportAsString = false // 保留原始型別 (DateTime, double, bool)
};
return cells.ExportDataTable(0, 0, totalRows, totalCols, options);
}
ExportColumnName:第一列是否為標題CheckMergedCells:合併儲存格值複製到每個子儲存格ExportAsString:false 保強型別 (推薦),true 全轉字串FormatStrategy:CellValueFormatStrategy.DisplayString 可套用儲存格格式 (如貨幣、百分比)針對 10MB+ 或 10萬列以上檔案,避免一次載入整個 Workbook:
public IEnumerable<DataRow> StreamReadLargeExcel(string filePath, int sheetIndex = 0)
{
var loadOptions = new LoadOptions(LoadFormat.Xlsx)
{
MemorySetting = MemorySetting.MemoryPreference, // 優先使用記憶體,不足則磁碟
ParsingFormulaOnOpen = false, // 不解析公式加速
OnlyLoadDocumentProperties = false
};
using (var workbook = new Workbook(filePath, loadOptions))
{
var sheet = workbook.Worksheets[sheetIndex];
var cells = sheet.Cells;
// 建立 DataTable 結構
var dt = new DataTable();
for (int c = cells.FirstColumn; c <= cells.MaxDataColumn; c++)
dt.Columns.Add(cells[0, c].StringValue); // 假設第一列為標題
// 使用 LightCells API 事件驅動逐列讀取 (Aspose.Cells 進階功能)
// 此處簡化示範:仍用 Cells 但分批處理
const int batchSize = 1000;
for (int r = 1; r <= cells.MaxDataRow; r += batchSize)
{
int end = Math.Min(r + batchSize - 1, cells.MaxDataRow);
for (int i = r; i <= end; i++)
{
var row = dt.NewRow();
for (int c = cells.FirstColumn; c <= cells.MaxDataColumn; c++)
row[c] = cells[i, c].Value;
dt.Rows.Add(row);
yield return row;
}
// 可選:釋放已處理區塊記憶體 (需 LightCells 完整實作)
}
}
}
| Excel 儲存格 | Cell.Value 型別 | 建議轉換 |
|---|---|---|
| 數值 (含日期) | double / DateTime | cell.Type == CellValueType.IsNumeric ? DateTime.FromOADate(val) : val |
| 布林 | bool | 直接使用 |
| 文字 | string | cell.StringValue (保留前導零) |
| 錯誤值 (#N/A) | string | cell.Value.ToString().StartsWith("#") 判斷 |
| 公式 | string (公式文本) / 計算後值 | workbook.CalculateFormula() 後取 cell.Value |
new License().SetLicense("Aspose.Total.lic") 放在程式啟動時執行一次,否則產出檔案會有評浮水印Aspose.Cells.dll,目標機器無需安裝 Excel| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| Aspose.Cells | 功能最全、效能強、無 Office 相依、商業支援 | 收費 (較貴) | 企業級專案、複雜報表、伺服器端批次處理 |
| EPPlus | 開源免費 (v5 起 Polyform Noncommercial)、API 直覺 | 僅支援 .xlsx、大檔案記憶體壓力大 | 中小專案、預算有限、.xlsx 為主 |
| NPOI | 免費、支援 .xls/.xlsx、HSSF/XSSF 雙引擎 | API 較底層、效能較慢、文件較少 | 需相容舊 .xls、無授權預算 |
| ClosedXML | 語法優雅、LINQ 風格、開源 | 僅 .xlsx、大檔案表現一般 | 快速開發、程式碼可讀性優先 |
掌握 Workbook → Worksheet → Cells → ExportDataTable 這條黃金路徑,配合 ExportTableOptions 微調匯出行為,即能以最少程式碼穩健解決 90% 的 Excel 匯入需求。若專案涉及超大檔案或高併發,再引入 LightCells 事件流或評估 EPPlus/NPOI 開源替代方案。