首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

.NET 操作 Excel 完全指南:三大主流方案深度解析與實戰代碼

     編輯:關於.NET

前言:為什麼需要在 .NET 中操作 Excel?

在企業級應用開發中,Excel 作為數據交換、報表匯出、批量導入的通用格式,佔據著不可替代的地位。許多開發者初次接觸時,容易混淆「在 Excel 中嵌入 .NET 代碼」(VSTO/OBA)與「在 .NET 應用程式中操作 Excel 文件」的區別。本文針對後者場景,系統梳理 .NET 操作 Excel 的三大主流技術路線,並提供可直接落地的核心代碼範例。

方案一:Microsoft.Office.Interop.Excel(COM 互操作)

核心特點

  • 優勢:功能最全面,可完整操作 Excel 物件模型(儲存格、圖表、樞紐分析表、VBA 宏等),所見即所得。
  • 劣勢:強依賴安裝完整版 Office Excel,不可部署於無 Office 的伺服器環境;效能較差,大量資料讀寫時明顯緩慢;易產生未釋放的 COM 物件導致記憶體洩漏,需嚴格管理生命週期。
  • 適用場景:桌面端應用、需複雜格式/圖表操作、用戶機已安裝 Office 的環境。

關鍵實作代碼

using Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;

// 單例模式管理 Application 實例,避免重複啟動
private static Application _excelApp;
private static Application GetExcelApp()
{
    if (_excelApp == null)
    {
        _excelApp = new ApplicationClass { Visible = false, DisplayAlerts = false };
    }
    return _excelApp;
}

public void ReadExcelViaCom(string filePath)
{
    Workbook workbook = null;
    Worksheet worksheet = null;
    try
    {
        var app = GetExcelApp();
        workbook = app.Workbooks.Open(filePath, ReadOnly: true);
        worksheet = (Worksheet)workbook.Sheets[1];

        // 範例:讀取 A 欄第 2 行起的非空單元格
        Range cell = (Range)worksheet.Cells[2, 1];
        var result = new System.Text.StringBuilder();
        for (int row = 2; cell.Value2 != null; row++)
        {
            result.Append(cell.Value2.ToString()).Append(". ");
            cell = (Range)worksheet.Cells[row, 1];
        }
        MessageBox.Show(result.ToString());
    }
    catch (Exception ex)
    {
        Console.WriteLine($"COM 讀取異常: {ex.Message}");
    }
    finally
    {
        // 嚴格釋放 COM 物件,順序不可亂
        if (workbook != null) { workbook.Close(false); Marshal.ReleaseComObject(workbook); }
        if (worksheet != null) Marshal.ReleaseComObject(worksheet);
        // 注意:生產環境中 Application 通常保持單例,程式退出時再 Quit 並 Release
    }
}

方案二:OleDb / ADO.NET(將 Excel 視為資料源)

核心特點

  • 優勢:無需安裝 Office,僅需安裝 Access Database Engine(ACE OLEDB Provider);以 SQL 查詢方式讀寫,代碼簡潔,適合批量資料匯入匯出;效能優於 COM 互操作。
  • 劣勢:僅能處理資料層面,無法操作格式、圖表、樞紐分析表等表現層元素;對合併儲存格、特殊資料類型支援有限;32/64 位元 Provider 需與應用程式位元數一致。
  • 適用場景:伺服器端批量資料導入導出、純資料交互、高效能讀寫需求。

連接字串關鍵點

  • Excel 2007+ (.xlsx):Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties='Excel 12.0 Xml;HDR=YES;IMEX=1'
  • Excel 97-2003 (.xls):Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};Extended Properties='Excel 8.0;HDR=YES;IMEX=1'
  • HDR=YES 表示第一行為標題;IMEX=1 強制混合類型欄位按文字讀取,避免資料截斷。

關鍵實作代碼

using System.Data;
using System.Data.OleDb;

public DataTable ReadExcelViaOleDb(string filePath, string sheetName = "Sheet1")
{
    var connStr = $@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={filePath};Extended Properties='Excel 12.0 Xml;HDR=YES;IMEX=1'";
    var dt = new DataTable();
    try
    {
        using (var conn = new OleDbConnection(connStr))
        {
            conn.Open();
            // 獲取工作表實際名稱(可選)
            // var schema = conn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
            // sheetName = schema.Rows[0]["TABLE_NAME"].ToString();

            using (var cmd = new OleDbCommand($"SELECT * FROM [{sheetName}]", conn))
            using (var adapter = new OleDbDataAdapter(cmd))
            {
                adapter.Fill(dt);
            }
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"OleDb 讀取異常: {ex.Message}");
    }
    return dt;
}

方案三:Open XML SDK(操作 Office Open XML 格式)

核心特點

  • 優勢:完全無 Office 依賴,官方推薦的伺服器端方案;直接操作 ZIP 封裝內的 XML,效能極高,支援超大文件(配合 SAX/Streaming 讀寫);可精確控制每個 XML 元素,功能極其強大。
  • 劣勢:學習曲線陡峭,需理解 Open XML 標準(ECMA-376)及文件結構;代碼量大,簡單操作也需編寫大量樣板代碼;不支援舊版 .xls 格式。
  • 適用場景:高併發伺服器端報表生成、超大文件處理、需精細控制樣式/結構、跨平臺部署(.NET Core / .NET 5+)。

核心類別速查

類別對應 XML 部分主要用途
SpreadsheetDocument整個 .xlsx 套件入口點,開啟/建立文件
WorkbookPartxl/workbook.xml工作簿層級:工作表管理、共用字串、樣式
WorksheetPartxl/worksheets/sheetN.xml單個工作表:儲存格資料、合併儲存格、資料驗證
SharedStringTablePartxl/sharedStrings.xml共用字串表,優化重複字串儲存
WorkbookStylesPartxl/styles.xml樣式定義:字型、填充、邊框、數字格式

關鍵實作代碼:讀取指定儲存格值

using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;

public string ReadCellValue(string filePath, string sheetName, string addressName)
{
    using (var doc = SpreadsheetDocument.Open(filePath, false))
    {
        var workbookPart = doc.WorkbookPart;
        var sheet = workbookPart.Workbook.Descendants<Sheet>()
            .FirstOrDefault(s => s.Name == sheetName);
        if (sheet == null) return null;

        var worksheetPart = (WorksheetPart)workbookPart.GetPartById(sheet.Id);
        var cell = worksheetPart.Worksheet.Descendants<Cell>()
            .FirstOrDefault(c => c.CellReference == addressName);
        if (cell == null) return null;

        string value = cell.CellValue?.InnerText;
        // 處理共用字串類型
        if (cell.DataType != null && cell.DataType.Value == CellValues.SharedString)
        {
            var stringTable = workbookPart.SharedStringTablePart.SharedStringTable;
            value = stringTable.ElementAt(int.Parse(value)).InnerText;
        }
        return value;
    }
}

關鍵實作代碼:寫入數值到儲存格


public void WriteCellValue(string filePath, string sheetName, string addressName, string value)
{
    using (var doc = SpreadsheetDocument.Open(filePath, true))
    {
        var workbookPart = doc.WorkbookPart;
        var sheet = workbookPart.Workbook.Descendants<Sheet>()
            .FirstOrDefault(s => s.Name == sheetName);
        if (sheet == null) return;

        var worksheetPart = (WorksheetPart)workbookPart.GetPartById(sheet.Id);
        var sheetData = worksheetPart.Worksheet.GetFirstChild<SheetData>();

        // 解析地址 (如 "B3") -> 欄索引、行索引
        var colName = new string(addressName.Where(char.IsLetter).ToArray());
        var rowIndex = uint.Parse(new string(addressName.Where(char.IsDigit).ToArray()));
        var colIndex = ColNameToIndex(colName);

        var row = sheetData.Elements<Row>().FirstOrDefault(r => r.RowIndex == rowIndex);
        if (row == null)
        {
            row = new Row { RowIndex = rowIndex };
            sheetData.Append(row);
        }

        var cell = row.Elements<Cell>().FirstOrDefault(c => c.CellReference.Value == addressName);
        if (cell == null)
        {
            cell = new Cell { CellReference = addressName };
            // 插入正確位置以維持順序
            var refCell = row.Elements<Cell>().FirstOrDefault(c => ColNameToIndex(GetColumnName(c.CellReference)) > colIndex);
            if (refCell != null) row.InsertBefore(cell, refCell);
            else row.Append(cell);
        }

        cell.CellValue = new CellValue(value);
        cell.DataType = CellValues.String; // 或 Number/Boolean 等
        worksheetPart.Worksheet.Save();
    }
}

private int ColNameToIndex(string colName) { /* 實現列名轉索引邏輯 */ }
private string GetColumnName(string cellRef) { /* 提取列名 */ }

三大方案綜合對比與選型建議

維度COM InteropOleDb / ADO.NETOpen XML SDK
Office 依賴必須安裝完整版需安裝 ACE Provider完全無依賴
部署環境僅限桌面/有 Office 的機器伺服器/桌面均可任意環境,含 Linux/Docker
功能覆蓋100% Excel 功能僅資料讀寫幾乎所有檔案結構/樣式
大數據效能差 (跨行程調用開銷大)良好 (流式讀寫)極佳 (DOM/SAX 雙模式)
開發難度低 (物件模型直觀)低 (SQL 風格)高 (需懂 XML 結構)
舊版 .xls 支援支援支援 (Jet Provider)不支援

選型決策樹

  1. 需操作圖表、樞紐分析表、VBA、列印設定等高階功能 → COM Interop (限桌面端)。
  2. 純資料導入導出、批量處理、伺服器端部署、不需複雜格式 → OleDb (最快上手)。
  3. 高併發報表生成、超大文件、跨平臺、精細樣式控制、無 Office 環境 → Open XML SDK (長遠最佳投資)。

進階補充:現代化替代方案

  • ClosedXML:Open XML SDK 的優雅封裝,API 近似 VBA,大幅降低學習成本,推薦作為 Open XML 入門首選。
  • EPPlus:功能豐富的商業庫 (5.0+ 版本為商業授權),支援圖表、樞紐分析表、條件格式等高階功能。
  • NPOI:Apache POI 的 .NET 移植,同時支援 .xls 和 .xlsx,成熟穩定,社區活躍。

最佳實踐總結

  1. 資源釋放:COM 物件必須 Marshal.ReleaseComObject;OleDb/Open XML 善用 using 區塊。
  2. 大數據分批:避免一次性載入百萬行到記憶體,採用分頁讀寫或 Streaming 模式 (Open XML OpenXmlReader/OpenXmlWriter)。
  3. 類型安全:OleDb 讀取時指定 IMEX=1;Open XML 寫入時正確設定 DataType。
  4. 錯誤處理:捕獲具體異常 (COMException, OleDbException, OpenXmlPackageException) 並記錄日誌。
  5. 測試驗證:針對合併儲存格、隱藏行/列、資料驗證、條件格式等邊界情況編寫自動化測試。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved