基於 DocumentFormat.OpenXml 操作 Excel (1)-- 初識


  最近抽空研究了一下 基於DocumentFormat.OpenXml操作Excel,也把自己的理解記錄下來,便於日后可以查閱。

  各種系統中,導出Excel是一種很常見的功能,在C#/.Net 環境下, 市面上存在很多相關的類庫,開源免費的有基於JAVAPOI移植過來的 NPOIEPPlusOpenXML 等等;也有收費的 如 Aspose.CellsSpire.XLS 等。

  收費的商業組件在性能,功能上會一般來說都會比較好,例如Aspose.Cells,在性能上是比較卓越的,價格最便宜也要1000美金左右。 但是大部分公司,特別是初創公司,考慮到成本問題,用得比較多的,可能還是會使用開源免費的 NPOIEPPlus,而OpenXML(DocumentFormat.OpenXml) 是微軟官方推出的一個操作Excel, Word, PPT文件的組件,而且操作的是更為底層的部分,能夠做到靈活和精確的控制,但是本身操作起來會比較復雜,有些順序的限制,會感覺沒有 NPOI 和 EPPlus 方便,而且生成出來的excel搞不好還會提示錯誤(有可能WPS打開不會,office打開會),而且性能方面雖然操作底層,但是也要看使用者本身,不一定性能可以很高。所以本身是比較少人會考慮使用它。

  這個組件為啥叫 OpenXML SDK,因為從07版本之后,office系列的文檔,包括Excel, Word, PPT這些文檔,都是使用XML方式存儲的。 而 Office 的 Open XML 文件格式規范是一個開放的國際性標准 ,是 ECMA 376 標准, 可以查閱這個網址:https://www.ecma-international.org/publications/standards/Ecma-376.htm 。 所以如果要學習這個SDK,則需要了解Excel里面 XML的組成元素,這個可以查閱 ECMA 376 標准的定義,但是會比較難啃。 但是了解Excel的內部組成,也對於你后期操作Excel的准確度,以及性能優化也是有幫助的。另外,由於是操作XML, 所以 OpenXML SDK只能適用與07版本后的office, 像2003版本的Excel 文件(.xls)是不支持的。

  在一個文檔的內部,都是由多份XML的東西來組成的。 我們可以自己新建一個Excel文檔(.xlsx),然后修改其后綴名 xlsx 為 rar/zip, 可以直接解壓。

  如下圖所示:

   

 

  

  從以上的圖中解壓出來后的文件,可以看出,一個 Excel (.xlsx)文件里面包含很多個XML文件,分別代表一個Excel不同模塊的數據組成。

  從名字上,我們大概初步可以猜測出幾點: 

  (1)workbook.xml 一個工作簿(Excel文件)的總覽

  (2)styles.xml 樣式相關

  (3)worksheets文件夾,包含多份xml, 每一份代表一個工作簿(Excel文件)里面其中的一個工作表(WorkSheet)

  (4)theme 文件夾,Excel主題相關

  (5)printerSettings文件夾, 打印設置相關

 

那么再來看看,初步通過代碼應該怎么實現。

微軟官方提供的文檔說明,可以查閱:https://docs.microsoft.com/zh-cn/office/open-xml/structure-of-a-spreadsheetml-document

OpenXML SDK 對應的Nuget包叫DocumentFormat.OpenXml,目前最新版本是 2.11.3,地址是: https://www.nuget.org/packages/DocumentFormat.OpenXml/   

OpenXML SDK 的相關類型說明,可以查閱微軟官方提供的API文檔:https://docs.microsoft.com/zh-cn/dotnet/api/documentformat.openxml.spreadsheet.workbook?view=openxml-2.8.1

在C#.Net 項目中,通過VS開發工具的Nuget管理工具安裝,也可以直接在終端通過nuget包管理命令安裝:  Install-Package DocumentFormat.OpenXml -Version 2.11.3 

 

 

根據微軟官方的文檔,先來通過C#代碼,簡單生成一個excel文件,並且保存到磁盤。 通過Nuget安裝好SDK。

根據微軟官方的介紹,一份最小(空白)工作簿必須包含以下內容,要:

    1、有一個工作表(WorkSheet)

    2、工作表的Id

    3、指向工作表定義位置的關系 Id 

 

可能聽起來有點難理解,通過這個來看下面的代碼示例(可以運行的)  

 1 using System;
 2 using System.IO;
 3 using DocumentFormat.OpenXml;
 4 using DocumentFormat.OpenXml.Packaging;
 5 using DocumentFormat.OpenXml.Spreadsheet;
 6 
 7 namespace PracticePart1
 8 {
 9     public class Program
10     {
11         public static void Main(string[] args)
12         {
13             //當前運行時路徑
14             var directoryInfo = new DirectoryInfo(Directory.GetCurrentDirectory());
15             var fileName = $@"PracticePart1-{DateTime.Now:yyyyMMddHHmmss}.xlsx";
16 
17             //文件路徑,保存在運行時路徑下
18             var filepath = Path.Combine(directoryInfo.ToString(), fileName);
19             Console.WriteLine($"FilePath: {filepath}");
20 
21             //創建SpreadsheetDocument對象,xlsx類型,通過路徑
22             var spreadsheetDocument = SpreadsheetDocument.Create(filepath, SpreadsheetDocumentType.Workbook);
23 
24             //通過Stream對象
25             //MemoryStream ms = new MemoryStream();
26             //SpreadsheetDocument.Create(ms, SpreadsheetDocumentType.Workbook);
27 
28             //調用AddWorkbookPart, 創建WorkbookPart對象, 創建Workbook對象(相當於XML根元素)關聯到WorkbookPart
29             var workbookPart = spreadsheetDocument.AddWorkbookPart();
30             workbookPart.Workbook = new Workbook();
31 
32             //通過上面的WorkbookPart,創建WorksheetPart對象,創建Worksheet對象(相當於XML根元素)關聯到 WorksheetPart
33             var worksheetPart = workbookPart.AddNewPart<WorksheetPart>();
34             worksheetPart.Worksheet = new Worksheet(new SheetData());
35 
36             // 創建Sheets 到 Workbook
37             var sheets = spreadsheetDocument.WorkbookPart.Workbook.AppendChild<Sheets>(new Sheets());
38 
39             // 創建添加Sheet對象, Id關聯 Worksheet, 從而命名工作表的名稱
40             var sheet = new Sheet()
41             {
42                 Id = spreadsheetDocument.WorkbookPart.GetIdOfPart(worksheetPart),
43                 SheetId = 1,
44                 Name = "myFirstSheet"
45             };
46 
47             //追加到 Sheets
48             sheets.Append(sheet);
49 
50             //保存到磁盤
51             workbookPart.Workbook.Save();
52 
53             // Close the document.
54             spreadsheetDocument.Close();
55         }
56     }
57 }

 

  以上是可以直接運行,然后生成Excel文件。上述涉及到的幾個類型,依據我個人的理解,下面簡單說明下。

  SpreadsheetDocument 表格文檔類, 從字面意思,它就表示一個Excel相關的文檔包。 通過它的Create方法, 通過枚舉來指定文檔類型(*.xlsx, 可以指定其它如*.xltx, *.xlsm等),同時指定文件路徑(重載方法中,是可以支持傳入一個Stream),創建一個document對象。然后創建表示 工作簿 的WorkbookPart, 創建表示 工作表的 WorksheetPart, 這個時候符合上訴的 第一個要求【有一個工作表(WorkSheet)】。

  接着通過工作簿對象Workbook創建一個Sheets,表示工作簿中的所有表。 這里要先明白Sheet Worksheet 的這2個的不同:

  (1)Worksheet : 表示的是工作簿的一份工作表的內容定義,就是我們打開Excel文件,下方顯示sheet1, sheet2,sheet3....的每一份工作表。

  (2)Sheet : 代表的是工作簿的一個表,它可以是工作表(Worksheet), 也可以圖表(ChartSheet),也可以是其它類型的表。它本身不涉及具體表怎么定義,但是會通過Id關聯具體表的定義; 所以 Sheets 是表示工作簿中關聯的所有表的集合(圖表,工作表,宏表等)。 比如一個Sheets,它其中包含2個工作表,1個圖表,而具體的定義放在其它的地方,一般一個表的定義有一個xml。 

  Sheet 對象, 有一個 IdSheetId, 這2個字段。 按照上訴的 第二個要求 【工作表的Id】,就是指 SheetId, 自己賦值。 第三個要求【指向工作表定義位置的關系 Id 】, 就是指這個Id字段, 這個Id 指向工作表定義位置(就是指Sheet 關聯到具體哪個 WorksheetPart ), 通過 WorkbookPart.GetIdOfPart 的方法來獲取這個Id

 

  以上就是DocumentFormat.OpenXml 創建一個最小化空白文檔的方式。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM