如何在未安裝 Excel 的情況下使用 C# 讀取 OpenOffice Excel 文件
使用正確的庫,在 C# 中讀取和處理 OpenDocument Spreadsheet (光學資料系統) 和 Excel 檔案非常簡單。 使用IronXL ,您可以透過一次方法呼叫將任何 XLS、XLSX、光學資料系統 或 CSV 檔案載入到 WorkBook 物件中—無需安裝 Microsoft Excel,無需 COM 註冊,也無需 Interop 方面的煩惱。 本指南將引導您完成每個步驟:安裝軟體包、載入檔案、提取鍵入的儲存格值、使用命名工作表、處理合併儲存格以及部署到 Linux 或容器化環境。
如何在.NET專案中安裝IronXL?
NuGet 套件管理器安裝
透過 NuGet 套件管理器將 IronXL 加入您的專案中。 在 Visual Studio 中開啟套件管理員控制台並執行:
Install-Package IronXL
dotnet add package IronXL
Install-Package IronXL
dotnet add package IronXL
IronXL 支援 .NET 8、.NET 9、.NET 10、.NET Framework 4.6.2+ 和 .NET Standard 2.0,因此它既適用於現代程式碼庫,也適用於舊版程式碼庫。安裝完成後,在任何處理電子表格的檔案頂部新增 using IronXL; 指令,即可載入您的第一個工作簿。
對於 Azure Functions、Docker 容器或 Linux 託管的 API,無需額外的執行時間配置。 該庫內部包含了所需的一切,無需調用 Excel 自動化元件。
如何在 C# 中載入 OpenOffice 或 Excel 檔案?
IronXL 透過 WorkBook.Load 方法以相同的方式處理 光學資料系統、XLS、XLSX 和 CSV 檔案。 你可以傳遞絕對或相對檔案路徑,函式庫會從檔案頭(而不僅僅是副檔名)偵測檔案格式。 這意味著將檔案從 .ods 重新命名為 .xlsx 仍然可以正確讀取。
using IronXL;
// Load an OpenDocument Spreadsheet (.ods) produced by LibreOffice Calc or OpenOffice
WorkBook workbook = WorkBook.Load("quarterly_report.ods");
// Access the first worksheet by index
WorkSheet sheet = workbook.WorkSheets[0];
// Read a cell value
string companyName = sheet["A1"].StringValue;
int recordCount = sheet["B1"].IntValue;
Console.WriteLine($"Company : {companyName}");
Console.WriteLine($"Records : {recordCount}");
using IronXL;
// Load an OpenDocument Spreadsheet (.ods) produced by LibreOffice Calc or OpenOffice
WorkBook workbook = WorkBook.Load("quarterly_report.ods");
// Access the first worksheet by index
WorkSheet sheet = workbook.WorkSheets[0];
// Read a cell value
string companyName = sheet["A1"].StringValue;
int recordCount = sheet["B1"].IntValue;
Console.WriteLine($"Company : {companyName}");
Console.WriteLine($"Records : {recordCount}");
Imports IronXL
' Load an OpenDocument Spreadsheet (.ods) produced by LibreOffice Calc or OpenOffice
Dim workbook As WorkBook = WorkBook.Load("quarterly_report.ods")
' Access the first worksheet by index
Dim sheet As WorkSheet = workbook.WorkSheets(0)
' Read a cell value
Dim companyName As String = sheet("A1").StringValue
Dim recordCount As Integer = sheet("B1").IntValue
Console.WriteLine($"Company : {companyName}")
Console.WriteLine($"Records : {recordCount}")
同樣的呼叫方法也適用於 XLSX 和 XLS 檔案——只需更改路徑即可。 WorkBook.Load 無論來源格式如何,都會返回相同的強類型對象,因此無論文件來自 Microsoft Excel、LibreOffice 還是任何其他符合 ODF 標準的應用程序,其餘代碼都保持不變。
如何讀取工作表中的每一行和每一儲存格?
遍歷行集合
遍歷所有行和儲存格是 Excel 中最常見的處理任務-無論是驗證匯入資料、轉換記錄或為報表管道提供資料。 IronXL 在每個 WorkSheet 上公開了一個 Rows 集合:
using IronXL;
WorkBook workbook = WorkBook.Load("customers.xlsx");
WorkSheet worksheet = workbook.WorkSheets[0];
Console.WriteLine($"Total rows : {worksheet.RowCount}");
Console.WriteLine($"Total columns : {worksheet.ColumnCount}");
Console.WriteLine();
foreach (var row in worksheet.Rows)
{
foreach (var cell in row)
{
if (!cell.IsEmpty)
Console.Write($"{cell.StringValue,-20}");
}
Console.WriteLine();
}
using IronXL;
WorkBook workbook = WorkBook.Load("customers.xlsx");
WorkSheet worksheet = workbook.WorkSheets[0];
Console.WriteLine($"Total rows : {worksheet.RowCount}");
Console.WriteLine($"Total columns : {worksheet.ColumnCount}");
Console.WriteLine();
foreach (var row in worksheet.Rows)
{
foreach (var cell in row)
{
if (!cell.IsEmpty)
Console.Write($"{cell.StringValue,-20}");
}
Console.WriteLine();
}
Imports IronXL
Dim workbook As WorkBook = WorkBook.Load("customers.xlsx")
Dim worksheet As WorkSheet = workbook.WorkSheets(0)
Console.WriteLine($"Total rows : {worksheet.RowCount}")
Console.WriteLine($"Total columns : {worksheet.ColumnCount}")
Console.WriteLine()
For Each row In worksheet.Rows
For Each cell In row
If Not cell.IsEmpty Then
Console.Write($"{cell.StringValue,-20}")
End If
Next
Console.WriteLine()
Next
RowCount 和 ColumnCount 屬性僅傳回已填滿的範圍-不包括空的尾隨行和列。 在讀取之前檢查 cell.IsEmpty 可以防止對稀疏工作表進行不必要的處理。
對於大文件,請考慮使用基於範圍的存取(worksheet["A1:D500"])而不是整頁迭代。 這樣可以限制載入到記憶體中的單元格數量,並在只需要部分資料時加快處理速度。
如何從儲存格中提取已輸入的值?
類型化財產評估員
現實世界中的電子表格單元格包含字串、整數、小數、布林值和日期。 IronXL 公開了專用的類型化屬性,因此您無需手動解析原始字串:
using IronXL;
WorkBook workbook = WorkBook.Load("inventory.xlsx");
WorkSheet sheet = workbook.GetWorkSheet("Products");
// Typed accessors handle conversion automatically
string productName = sheet["A2"].StringValue;
int quantity = sheet["B2"].IntValue;
decimal unitPrice = sheet["C2"].DecimalValue;
bool inStock = sheet["D2"].BoolValue;
DateTime? lastAudit = sheet["E2"].DateTimeValue;
Console.WriteLine($"Product : {productName}");
Console.WriteLine($"Qty : {quantity}");
Console.WriteLine($"Price : {unitPrice:C}");
Console.WriteLine($"In Stock : {inStock}");
Console.WriteLine($"Audited : {lastAudit?.ToString("d") ?? "Never"}");
using IronXL;
WorkBook workbook = WorkBook.Load("inventory.xlsx");
WorkSheet sheet = workbook.GetWorkSheet("Products");
// Typed accessors handle conversion automatically
string productName = sheet["A2"].StringValue;
int quantity = sheet["B2"].IntValue;
decimal unitPrice = sheet["C2"].DecimalValue;
bool inStock = sheet["D2"].BoolValue;
DateTime? lastAudit = sheet["E2"].DateTimeValue;
Console.WriteLine($"Product : {productName}");
Console.WriteLine($"Qty : {quantity}");
Console.WriteLine($"Price : {unitPrice:C}");
Console.WriteLine($"In Stock : {inStock}");
Console.WriteLine($"Audited : {lastAudit?.ToString("d") ?? "Never"}");
Imports IronXL
Dim workbook As WorkBook = WorkBook.Load("inventory.xlsx")
Dim sheet As WorkSheet = workbook.GetWorkSheet("Products")
' Typed accessors handle conversion automatically
Dim productName As String = sheet("A2").StringValue
Dim quantity As Integer = sheet("B2").IntValue
Dim unitPrice As Decimal = sheet("C2").DecimalValue
Dim inStock As Boolean = sheet("D2").BoolValue
Dim lastAudit As DateTime? = sheet("E2").DateTimeValue
Console.WriteLine($"Product : {productName}")
Console.WriteLine($"Qty : {quantity}")
Console.WriteLine($"Price : {unitPrice:C}")
Console.WriteLine($"In Stock : {inStock}")
Console.WriteLine($"Audited : {If(lastAudit?.ToString("d"), "Never")}")
當儲存格包含公式時,IronXL 會對其進行計算,並透過相同的類型屬性傳回計算結果。 您無需呼叫單獨的評估方法。 對於可能為空或包含不相容類型的儲存格,該程式庫會傳回該類型的預設值,而不是拋出異常,從而簡化輸入驗證邏輯。
| 屬性 | .NET 類型 | 傳回空值 | 注意事項 |
|---|---|---|---|
StringValue |
`字串編碼 | 空字串 | 始終安全;可將任何單元格轉換為文字 |
IntValue |
int |
0 |
截斷小數 |
DecimalValue |
decimal |
0m |
保持財務數據的精確性 |
DoubleValue |
double |
0.0 |
用於科學或統計值 |
BoolValue |
bool |
false |
讀取 Excel 中的 TRUE/FALSE 儲存格 |
DateTimeValue |
DateTime? |
null |
可空——使用前請檢查 |
如何處理多個已命名的工作表?
按名稱、索引或迭代訪問
企業電子表格通常包含多個命名工作表-例如,每月一個工作表、每個地區一個工作表或每個產品線一個工作表。 IronXL 為您提供了多種存取這些工作表的方式:
using IronXL;
WorkBook workbook = WorkBook.Load("annual_sales.xlsx");
// Option 1: access by name (throws if the sheet does not exist)
WorkSheet januarySheet = workbook.GetWorkSheet("January");
// Option 2: iterate all sheets in the workbook
foreach (WorkSheet ws in workbook.WorkSheets)
{
Console.WriteLine($"Sheet: {ws.Name} | Rows: {ws.RowCount}");
// Read the header row from each sheet
string header = ws["A1"].StringValue;
Console.WriteLine($" Header: {header}");
}
// Option 3: access by zero-based index
WorkSheet lastSheet = workbook.WorkSheets[workbook.WorkSheets.Count - 1];
Console.WriteLine($"Last sheet: {lastSheet.Name}");
using IronXL;
WorkBook workbook = WorkBook.Load("annual_sales.xlsx");
// Option 1: access by name (throws if the sheet does not exist)
WorkSheet januarySheet = workbook.GetWorkSheet("January");
// Option 2: iterate all sheets in the workbook
foreach (WorkSheet ws in workbook.WorkSheets)
{
Console.WriteLine($"Sheet: {ws.Name} | Rows: {ws.RowCount}");
// Read the header row from each sheet
string header = ws["A1"].StringValue;
Console.WriteLine($" Header: {header}");
}
// Option 3: access by zero-based index
WorkSheet lastSheet = workbook.WorkSheets[workbook.WorkSheets.Count - 1];
Console.WriteLine($"Last sheet: {lastSheet.Name}");
Imports IronXL
Dim workbook As WorkBook = WorkBook.Load("annual_sales.xlsx")
' Option 1: access by name (throws if the sheet does not exist)
Dim januarySheet As WorkSheet = workbook.GetWorkSheet("January")
' Option 2: iterate all sheets in the workbook
For Each ws As WorkSheet In workbook.WorkSheets
Console.WriteLine($"Sheet: {ws.Name} | Rows: {ws.RowCount}")
' Read the header row from each sheet
Dim header As String = ws("A1").StringValue
Console.WriteLine($" Header: {header}")
Next
' Option 3: access by zero-based index
Dim lastSheet As WorkSheet = workbook.WorkSheets(workbook.WorkSheets.Count - 1)
Console.WriteLine($"Last sheet: {lastSheet.Name}")
如果在設計時已知圖紙名稱,則 GetWorkSheet 是最清晰的選擇。 對於動態處理(其中工作表名稱來自使用者輸入或配置),迭代 WorkSheets 集合可以防止硬編碼假設,並處理工作表數量不同的工作簿。
如何處理合併儲存格和格式化區域?
報表和儀表板經常使用合併儲存格作為標題、分組標籤和總計行。 IronXL 會讀取合併區域左上角儲存格的值,與 Excel 顯示的值完全相同:
using IronXL;
WorkBook workbook = WorkBook.Load("report_with_merges.xlsx");
WorkSheet sheet = workbook.DefaultWorkSheet;
// The merged region A1:D1 stores its value in cell A1
string reportTitle = sheet["A1"].StringValue;
Console.WriteLine($"Report title : {reportTitle}");
// Read cell formatting metadata
var titleCell = sheet["A1"];
Console.WriteLine($"Bold : {titleCell.Style.Font.Bold}");
Console.WriteLine($"Font size : {titleCell.Style.Font.Height}");
// Scan an entire column for non-empty section headers
foreach (var cell in sheet["A1:A100"])
{
if (!cell.IsEmpty && cell.Style.Font.Bold)
Console.WriteLine($"Section header at {cell.AddressString}: {cell.StringValue}");
}
using IronXL;
WorkBook workbook = WorkBook.Load("report_with_merges.xlsx");
WorkSheet sheet = workbook.DefaultWorkSheet;
// The merged region A1:D1 stores its value in cell A1
string reportTitle = sheet["A1"].StringValue;
Console.WriteLine($"Report title : {reportTitle}");
// Read cell formatting metadata
var titleCell = sheet["A1"];
Console.WriteLine($"Bold : {titleCell.Style.Font.Bold}");
Console.WriteLine($"Font size : {titleCell.Style.Font.Height}");
// Scan an entire column for non-empty section headers
foreach (var cell in sheet["A1:A100"])
{
if (!cell.IsEmpty && cell.Style.Font.Bold)
Console.WriteLine($"Section header at {cell.AddressString}: {cell.StringValue}");
}
Imports IronXL
Dim workbook As WorkBook = WorkBook.Load("report_with_merges.xlsx")
Dim sheet As WorkSheet = workbook.DefaultWorkSheet
' The merged region A1:D1 stores its value in cell A1
Dim reportTitle As String = sheet("A1").StringValue
Console.WriteLine($"Report title : {reportTitle}")
' Read cell formatting metadata
Dim titleCell = sheet("A1")
Console.WriteLine($"Bold : {titleCell.Style.Font.Bold}")
Console.WriteLine($"Font size : {titleCell.Style.Font.Height}")
' Scan an entire column for non-empty section headers
For Each cell In sheet("A1:A100")
If Not cell.IsEmpty AndAlso cell.Style.Font.Bold Then
Console.WriteLine($"Section header at {cell.AddressString}: {cell.StringValue}")
End If
Next
Style 屬性樹反映了OOXML SpreadsheetML 規範的結構,因此如果您使用過 Open XML SDK,屬性名稱會感覺很熟悉。 然而,IronXL 將所有這些複雜性封裝在一個簡潔的 API 中,而無需您進行任何 XML 操作。
如何使用同一個API匯入CSV檔案?
資料庫匯出、CRM 系統和舊版應用程式產生的 CSV 檔案可以透過相同的 WorkBook.Load 呼叫讀取。 IronXL 從檔案內容推斷分隔符號:
using IronXL;
// Load a comma-separated values file -- same method, same API
WorkBook csvWorkbook = WorkBook.Load("export.csv");
WorkSheet csvSheet = csvWorkbook.DefaultWorkSheet;
Console.WriteLine($"CSV rows loaded: {csvSheet.RowCount}");
// Process rows exactly like any other worksheet
foreach (var row in csvSheet.Rows)
{
string id = row[0].StringValue;
string name = row[1].StringValue;
Console.WriteLine($"{id,-10} {name}");
}
using IronXL;
// Load a comma-separated values file -- same method, same API
WorkBook csvWorkbook = WorkBook.Load("export.csv");
WorkSheet csvSheet = csvWorkbook.DefaultWorkSheet;
Console.WriteLine($"CSV rows loaded: {csvSheet.RowCount}");
// Process rows exactly like any other worksheet
foreach (var row in csvSheet.Rows)
{
string id = row[0].StringValue;
string name = row[1].StringValue;
Console.WriteLine($"{id,-10} {name}");
}
Imports IronXL
' Load a comma-separated values file -- same method, same API
Dim csvWorkbook As WorkBook = WorkBook.Load("export.csv")
Dim csvSheet As WorkSheet = csvWorkbook.DefaultWorkSheet
Console.WriteLine($"CSV rows loaded: {csvSheet.RowCount}")
' Process rows exactly like any other worksheet
For Each row In csvSheet.Rows
Dim id As String = row(0).StringValue
Dim name As String = row(1).StringValue
Console.WriteLine($"{id,-10} {name}")
Next
載入完成後,您可以使用 csvWorkbook.SaveAs("output.xlsx") 將資料儲存為 XLSX。 這是 CSV 到 Excel 轉換管道的常見模式——接收 CSV 文件,用計算列或格式豐富它,並將格式化的 XLSX 報告返回給用戶。
對於製表符分隔的檔案或自訂分隔符,請使用 WorkBook.LoadCSV("file.tsv", file格式: ExcelFile格式.TSV) 明確指定格式。
| 格式 | 擴充功能 | 出品方 | 注意事項 |
|---|---|---|---|
| XLSX | .xlsx |
Excel 2007+、LibreOffice | 預設現代格式;基於 XML |
| XLS | .xls |
Excel 97-2003 | 二進制格式;完全讀取/寫入支持 |
| 光學資料系統 | .ods |
LibreOffice、OpenOffice | OpenDocument電子表格標準 |
| CSV | .csv |
任何申請 | 分隔符號自動偵測;無格式化 |
| TSV | .tsv |
資料庫匯出 | 製表符分隔;請明確指定格式。 |
IronXL 與 Microsoft.Office.Interop.Excel 相比如何?
Interop、Open XML SDK 和 IronXL 並排顯示
在 .NET 中,IronXL 最常見的 Excel 自動化替代方案是Microsoft.Office.Interop.Excel 。 了解各種優缺點有助於您為專案選擇合適的工具。
Microsoft Interop 對 Excel COM 物件模型進行了封裝。 這意味著必須在執行程式碼的每台機器上安裝 Excel,包括 Web 伺服器、建置代理程式和雲端虛擬機器。 COM 物件生命週期管理是手動的:您必須明確釋放每個 Range、Worksheet 和 Workbook 對象,否則 Excel 進程會在後台累積並消耗記憶體,直到伺服器重新啟動。 許可證也是一個需要考慮的問題:Office EULA 在許多情況下禁止伺服器端自動化。
IronXL 避免了所有這些限制。 這是一個純粹的託管庫,不依賴 COM。 WorkBook 物件是一個標準的 .NET 類別; 垃圾回收器負責清理工作。你可以在開發人員的筆記型電腦、Azure 應用程式服務、Docker 容器或執行 Linux 的 Raspberry Pi 上執行相同的程式碼。
微軟的Open XML SDK是另一個選擇。它無需 Excel 即可直接存取 OOXML 檔案格式,但它的操作層級非常低——您需要直接操作 XML 元素。 讀取單一儲存格值需要遍歷共用字串表、儲存格參考和樣式索引。 IronXL 將所有這些封裝到本指南中所示的單行 sheet["A1"].StringValue 呼叫中。
如何在 Linux 和 Docker 上部署 Excel Processing?
在伺服器部署中,IronXL 獨立於 Excel 的優勢體現得最為明顯。 在 Windows 上編寫的相同程式碼,在 Ubuntu、Alpine Linux 或 macOS 上無需修改即可運行。 對於容器化部署,您的 Dockerfile 不需要任何特殊配置:
# Standard .NET runtime image -- no Office packages needed
FROM mcr.microsoft.com/dotnet/runtime:10.0
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "ExcelProcessor.dll"]
# Standard .NET runtime image -- no Office packages needed
FROM mcr.microsoft.com/dotnet/runtime:10.0
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "ExcelProcessor.dll"]
對於Azure Functions和 AWS Lambda,IronXL 可在託管運行時中運行,無需任何額外配置。 由於沒有 COM 初始化步驟,冷啟動開銷極小。
線程安全是內建的:多個線程可以同時打開不同的 WorkBook 實例而無需協調。 如果您需要並行處理數千個檔案(例如,在背景作業中處理使用者上傳的電子表格),則可以在 Parallel.ForEach 或 Task.WhenAll 實例池中使用 WorkBook 實例,而不會有損壞的風險。
由於 IronXL 在初始化時只將請求的工作表載入到記憶體中,而不是將整個工作簿載入到記憶體中,因此記憶體使用情況保持可預測性。 對於非常大的文件,這種差異很重要:一個包含十個 50MB 工作表的簿子並不需要 500MB 的 RAM 來讀取單一工作表。 IronXL 效能文件涵蓋了處理大容量文件處理場景的其他策略。
下一步計劃是什麼?
在專案中驗證 IronXL 最快的方法是安裝 NuGet 套件,然後針對環境中的真實檔案執行本指南中的範例。 免費試用許可證可解鎖所有功能,無需更改代碼即可在準備投入生產時使用。
探索 IronXL 的其他相關功能,這些功能可以補充文件讀取功能:
使用 C# 編寫和建立 Excel 檔案-以程式設計方式建立報表和資料匯出 -設定儲存格、行和列的格式-透過程式碼套用顏色、字型和邊框。 -使用 C# 處理 Excel 公式-讀取和寫入公式儲存格 -在 .NET 中將 Excel 轉換為 PDF -- 從電子表格資料產生 PDF 報告 IronXL API 文件-所有類別和方法的完整參考 IronXL 的授權和定價——個人開發者和企業團隊的選項 IronXL NuGet 頁面-- 所有項目類型的安裝說明
- IronXL 教學-常見用例的逐步指南
- IronXL 與 EPPlus 的對比——功能和許可對比 IronXL 產品主頁-功能概述和入門資源
對於許可、跨平台支援或特定文件格式要求方面的問題, IronXL 支援團隊可透過線上聊天和電子郵件提供協助。
!{--01001100010010010100001001010010010000010101001001011001010111110100011101000101010101 01000101111101010011010101000100000101010010010101000100010101000100010111110101011101001000110 1010101000100100001011111010100000101001001001111010001000101010101010000110101010100101010101011 10101010001010010010010010010000010100110001011111010000100100110001001111101000011010010111111010000110100101110--
常見問題解答
如何在未安裝 Excel 的情況下,使用 C# 讀取 OpenOffice Excel 檔案?
您可以使用 IronXL程式庫在 C# 中讀取 OpenOffice Excel 檔案,無需在伺服器上安裝 Excel。它允許您透過單一 WorkBook.Load 方法呼叫,高效載入 XLS、XLSX、ODS 和 CSV 檔案。
IronXL 可以處理哪些類型的 Excel 檔案?
IronXL 支援處理多種 Excel 檔案格式,包括 XLS、XLSX、ODS(OpenDocument 試算表)及 CSV 檔案,使其能廣泛應用於 LibreOffice 和 OpenOffice 等各類試算表應用程式。
開發人員為何應使用 IronXL 而非 Microsoft Interop 來讀取 Excel 檔案?
IronXL 提供了一種更簡單、更高效的方式來處理 Excel 檔案,無需使用 Microsoft Excel 或 COM 互通技術,從而降低生產伺服器的複雜度與開銷,並支援 Linux 和 Docker 部署。
IronXL 能否處理非 MS Excel 製成的試算表檔案?
是的,IronXL 能夠讀取並處理來自其他試算表應用程式的檔案,例如 OpenOffice Calc 和 LibreOffice Calc,使其成為開發者處理 ODS 和 ODF 相容格式時的靈活工具。
是否必須在生產伺服器上安裝 Excel 才能使用 IronXL?
不,使用 IronXL 時,您無需在生產伺服器上安裝 Excel,這簡化了部署流程並降低了維護需求。IronXL 可在 Windows、Linux、macOS 以及 Docker 容器內運行。
IronXL 是否支援處理 CSV 檔案?
是的,IronXL 完全支援讀取和處理 CSV 檔案,並可透過相同的 WorkBook.Load API 處理其他 Excel 格式,例如 XLS、XLSX 和 ODS。
對於 .NET 開發者而言,使用 IronXL for .NET 有什麼好處?
IronXL for .NET 提供了一個易於使用的程式庫,無需 Microsoft Excel 即可讀取、寫入及處理 Excel 檔案,透過簡潔且強類型化的 API,提升生產力並縮短開發時間。
IronXL 如何提升處理 Excel 檔案的效率?
IronXL 透過消除對 Excel 軟體的需求來提升效率,提供按類型定義的儲存格值存取器、執行緒安全的並行處理,以及可在 .NET 應用程式內直接處理 Excel 檔案的輕量級解決方案。


