- 积分
- 136119
- 注册时间
- 2014-12-27
- 最后登录
- 2024-3-28
- 在线时间
- 602 小时
- 威望
- 562
- 贡献
- 29
- 金币
- 52623
- 钢镚
- 1422
- 交易凭证
- 1
- 分享
- 0
- 精华
- 33
- 帖子
- 2094
- 主题
- 1742
TA的每日心情 | 擦汗 2018-4-10 15:18 |
---|
签到天数: 447 天 [LV.9]以坛为家II
超级版主
- 威望
- 562
- 贡献
- 29
- 金币
- 52623
- 钢镚
- 1422
|
在我最近开发的一个网页查询的项目中,客户提供的数据是多个 Excel 2007 文件,这些文件都很大,有的有十几万行(注意:Excel 2003 文件不能超过 65,536 行)。这些 Excel 2007 文件需要定期批量转换为网页程序可以读取的专用二进制格式文件。我们知道,Microsoft Office System 2007 引入了一个新的文件格式:Office Open XML 格式。她是基于 XML 和 ZIP 归档技术创建的,可以使用任何平台的能够处理 XML 或者 ZIP 文件的工具来访问并且修改文档内容。所以我们就可以使用 Microsoft .NET Framework 2.0 的强大 XML 类库来读取 Excel 2007 文件并转换为网页程序所需的专用二进制格式文件。当然,也可以使用 System.IO.Packaging 名称空间中的类库,但是她位于 .NET Framework 3.0 SDK (WinFX) 的 WindowsBase.dll 中。微软网站上有几篇很有用的文章:“Office (2007) Open XML 文件格式简介”和“如何操作 Office Open XML 格式文档”。
下面,就来看看 Excel 2007 Open XML 文件的结构吧:
上图是一个名为 test1.xlsx 的 Excel 2007 文件。我没有 Office 2007 软件,只有正版的 Office 2003 软件。所以需要到微软网站下载一个“Microsoft Office Word、Excel 和 PowerPoint 2007 文件格式兼容包”,就可以在 Office 2003 中编辑 Office Open XML 文档了。test1.xlsx 文件其实是一个 zip 文件。为了分析其结构,我们现在把她解压到 D:/Test/test1/ 目录下。第一个重要的文件是 xl/workbook.xml,如下图所示:
该文件中的每个“<sheet>”元素都代表 Excel 2007 文件中的一个工作表,工作表的名称就是其“name”属性的值,在上图中是“好人”和“坏人”。然后根据“<sheet>”元素“r:id”属性的值(如上图中的“rId1”)到 xl/_rels/workbook.xml.rels 文件中寻找相应工作表数据实际存放的 xml 文件,如下图所示:
从图中可以看中,第一个工作表“好人”的数据实际存放在 worksheets/sheet1.xml 文件中,该文件的内容如下图所示:
上图中的“<dimension>”元素的“ref”属性的值(“B2:C4”)表示该工作表的范围。“<sheetData>”元素表示工作的数据,其子元素“<row>”表示工作表中的一行,“<row>”的子元素“<c>”表示该行中的单元格。如果“<c>”元素有“t”属性的话,“<c>”元素的子元素“<v>”的值就是各工作表共享的字符串的索引。否则的话,“<v>”元素的值就是该单元格的值。各工作表共享的字符串存放在 xl/sharedStrings.xml 文件中,如下图所示:
上图中,“<sst>”元素的子元素“<si>”就代表了共享的字符串,其值就是“<si>”元素的子元素“<t>”的值。
下面就看看我们的测试程序吧:
源程序的整体结构如下图所示:
我们先看看 XlsxFile.cs 吧:
- using System;
- using System.IO;
- using System.Xml;
- using Skyiv.Ben.Common;
-
- namespace Skyiv.OfficeHelper
- {
- /// <summary>
- /// Excel 2007 文件
- /// </summary>
- sealed partial class XlsxFile : IDisposable
- {
- string fileName; // Excel 2007 文件的文件名
- Sheet[] sheets; // Excel 2007 文件的各工作表
- FileStream fileStream { get { return new FileStream(fileName, FileMode.Open, FileAccess.Read); } }
-
- /// <summary>
- /// Excel 2007 文件的构造函数
- /// </summary>
- /// <param name="fileName">Excel 2007 文件的文件名</param>
- public XlsxFile(string fileName)
- {
- this.fileName = fileName;
- }
-
- /// <summary>
- /// Excel 2007 文件的各工作表
- /// </summary>
- public Sheet[] Sheets
- {
- get
- {
- if (sheets == null)
- {
- using (Stream zs = Zip.GetZipInputStream(fileStream, "xl/workbook.xml"))
- {
- // xl/workbook.xml 文件的内容举例如下:
- // <workbook>
- // <sheets>
- // <sheet name="好人" sheetId="1" r:id="rId1" />
- // <sheet name="坏人" sheetId="2" r:id="rId2" />
- // </sheets>
- // </workboo>
- XmlDocument xmlDocument = new XmlDocument();
- xmlDocument.Load(zs);
- XmlNodeList elms = xmlDocument.DocumentElement["sheets"].ChildNodes;
- sheets = new Sheet[elms.Count];
- for (int i = 0; i < elms.Count; i++)
- {
- XmlAttributeCollection attrs = elms[i].Attributes;
- sheets[i] = new Sheet(attrs["name"].Value, GetXmlFileName(attrs["r:id"].Value), SharedStrings, fileStream);
- }
- }
- }
- return sheets;
- }
- }
-
- /// <summary>
- /// 根据“标识”给出表示工作表的 XML 文件名
- /// </summary>
- /// <param name="id">标识</param>
- /// <returns>表示工作表的 XML 文件名</returns>
- string GetXmlFileName(string id)
- {
- string value;
- using (Stream zs = Zip.GetZipInputStream(fileStream, "xl/_rels/workbook.xml.rels"))
- {
- // xl/_rels/workbook.xml.rels 文件的内容举例如下:
- // <Relationships>
- // <Relationship Id="rId1" Target="worksheets/sheet1.xml" />
- // <Relationship Id="rId2" Target="worksheets/sheet2.xml" />
- // </Relationships>
- XmlDocument xmlDocument = new XmlDocument();
- xmlDocument.Load(zs);
- value = XmlHelper.GetElementById(xmlDocument, id).Attributes["Target"].Value;
- }
- return value;
- }
-
- public void Dispose()
- {
- if (sheets == null) return;
- foreach (Sheet sheet in sheets) sheet.Dispose();
- }
- }
- }
-
复制代码 该程序已经有很详细的注释了。在该程序中用 XmlDocument 类来读 xl/workbook.xml 文件和 xl/_rels/workbook.xml.rels 文件,是因为这两个文件都是非常小的文件。然后再来看看 XlsxFile.SharedStrings.cs 吧:
- using System;
- using System.IO;
- using System.Xml;
- using Skyiv.Ben.Common;
-
- namespace Skyiv.OfficeHelper
- {
- partial class XlsxFile
- {
- string[] sharedStrings;
-
- /// <summary>
- /// Excel 2007 文件中各工作表共享的字符串
- /// </summary>
- string[] SharedStrings
- {
- get
- {
- if (sharedStrings == null)
- {
- Stream zs = null;
- try
- {
- zs = Zip.GetZipInputStream(fileStream, "xl/sharedStrings.xml"); // 可能引发(FileNotFoundException)
- // xl/sharedStrings.xml 文件的内容举例如下:
- // <sst count="56" uniqueCount="2">
- // <si><t>任盈盈</t><phoneticPr fontId="1" type="noConversion" /></si>
- // <si><t /></si>
- // </sst>
- using (XmlReader reader = XmlReader.Create(zs))
- {
- while (reader.Read()) if (reader.IsStartElement("sst")) break;
- sharedStrings = new string[Convert.ToInt32(reader.GetAttribute("uniqueCount"))];
- for (int count = 0; ; count++)
- {
- reader.Read(); // 执行后(reader)的值: <si> or </sst>
- if (!reader.IsStartElement("si")) break;
- reader.ReadStartElement("si"); // 执行后(reader)的值: <t> or <t />
- sharedStrings[count] = reader.ReadElementString("t").Trim(); // 执行后(reader)的值: </si> or <与<t>同级的元素>
- if (reader.NodeType != XmlNodeType.EndElement) reader.ReadToNextSibling("t"); // 执行后(reader)的值: </si>
- }
- }
- }
- catch (FileNotFoundException)
- {
- sharedStrings = new string[0]; // 如果没有找到 xl/sharedStrings.xml 文件
- }
- finally
- {
- if (zs != null) zs.Close();
- }
- }
- return sharedStrings;
- }
- }
- }
- }
-
复制代码 这下必须用 XmlReader 类来读取 xl/sharedStrings.xml 了,因为这个 xml 文件可能很大。最后是 XlsxFile.Sheet.cs 了:
在这个程序中也是用 XmlReader 类来读取 xl/worksheets/sheet1.xml 文件。
完整的源程序可到以下地址下载:
http://files.cnblogs.com/skyivben/OpenXmlTest-src.7z
编译后的可执行文件的下载地址:
http://files.cnblogs.com/skyivben/OpenXmlTest-bin.7z
本程序需要 ICSharpCode.SharpZipLib 0.85.4.369,可到以下网址下载:
http://www.icsharpcode.net/OpenSource/SharpZipLib/Download.aspx
本文来自:http://www.cnblogs.com/skyivben/archive/2007/09/23/903582.html
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|