e狐网络 ——Powered by e-fox.net
基于OOXML技术的复杂Excel文件在线输出(我的硕士论文)
来自:efox@ e狐网络 日期:2009-5-28 11:45:11 全文阅读:loading... 分类:工程硕士
【摘要】
该文发布于CSDN,2008-1-7,当时正在准备毕业论文。今天偶然看到,还是发到这里吧。
CSDN原文:基于OOXML (Office Open XML)的复杂Excel文件在线输出1(动态复制表单)
还有一篇发布在CodeProject上:Copy Excel Sheet - for Excel 2007 (2008-1-14)
写在前面的:如果你是OOXML的反对者,本文可能不适合你。
概要
    大家一定知道微软最新的OOXML吧,虽然去年没有被设立为ISO标准(现在已经是了),但是它的思路和做法对我们开发人员还是有很大帮助的。本问想阐述的是如何利用OOXML实现在我们项目中复杂Excel文件的在线输出。

项目需求
    在线导出Excel文件是项目项目开发中的常用功能,也是重要功能,且当导出的Excel文件构成比较复杂,特别是程序要能根据用户的请求数据自动判断表单数目,动态完成Excel表单的复制和数据绑定,最终提供用户下载就更加困难。
    在尝试以往各种方法后,我们在项目中决定采用Excel2007的格式开发,即OOXML,众所周知OOXML是一个新的文件类型,确切的说是一个压缩包,而压缩包里面是组成该文件的XML部件文档,一旦面向XML文件了,所有问题就变得简单了。

实现步骤
 
……


【全文】
该文发布于CSDN,2008-1-7,当时正在准备毕业论文。今天偶然看到,还是发到这里吧。
CSDN原文:基于OOXML (Office Open XML)的复杂Excel文件在线输出1(动态复制表单)
还有一篇发布在CodeProject上:Copy Excel Sheet - for Excel 2007 (2008-1-14)
写在前面的:如果你是OOXML的反对者,本文可能不适合你。
概要
    大家一定知道微软最新的OOXML吧,虽然去年没有被设立为ISO标准(现在已经是了),但是它的思路和做法对我们开发人员还是有很大帮助的。本问想阐述的是如何利用OOXML实现在我们项目中复杂Excel文件的在线输出。

项目需求
    在线导出Excel文件是项目项目开发中的常用功能,也是重要功能,且当导出的Excel文件构成比较复杂,特别是程序要能根据用户的请求数据自动判断表单数目,动态完成Excel表单的复制和数据绑定,最终提供用户下载就更加困难。
    在尝试以往各种方法后,我们在项目中决定采用Excel2007的格式开发,即OOXML,众所周知OOXML是一个新的文件类型,确切的说是一个压缩包,而压缩包里面是组成该文件的XML部件文档,一旦面向XML文件了,所有问题就变得简单了。

实现步骤
 
    1、对Office 2007格式文件包操作
      由于Office 2007的文件是基于 XML 和 ZIP 归档技术创建的,所以我们可以使用任何能够处理 XML 或者 ZIP 文件的工具来访问并且修改文档内容。可使用ICSharpCode.SharpZipLib 下载地址:
http://www.icsharpcode.net/OpenSource/SharpZipLib/Download.aspx,也可以使用 System.IO.Packaging 名称空间中的类库,但需要安装Framework3.0,并引用 WindowsBase.dll。
      2、完成表单复制代码
/// <summary>
       
/// 拷贝sheet
       
/// </summary>
       
/// <param name="fileName">excel文件</param>
       
/// <param name="sheetName">待拷贝的sheet名称</param>
       
/// <param name="fileNo">流水号,用于名称后的数字</param>
       
/// <returns>新产生的sheet名称</returns>
        protected string CopySheet(string fileName, string sheetName, int fileNo)
        {
           
string partName = "/xl/workbook.xml";
           
string relFile = "/xl/_rels/workbook.xml.rels";
           
//打开包==============================================
            Package xlPackage = Package.Open(fileName, FileMode.Open, FileAccess.ReadWrite);
           
           
// 出异常时返回"newSheetName",则:如果在try{}已产生名字,也可以返回

           
string newSheetName = "";
           
           
try
            {
                Uri documentUri
= new Uri(partName, UriKind.Relative);
                PackagePart documentPart
= xlPackage.GetPart(documentUri);
               
//读出workbook.xml=======================================
                XmlDocument doc = new XmlDocument();
                doc.Load(documentPart.GetStream());
                XmlNamespaceManager nsManager
= new XmlNamespaceManager(doc.NameTable);
                nsManager.AddNamespace(
"d", doc.DocumentElement.NamespaceURI);

               
string searchString = string.Format("//d:sheet[@name='{0}']", sheetName);
                XmlNode node
= doc.SelectSingleNode(searchString, nsManager);

               
if (node == null)
                   
return null; //指定的sheet不存在
                else
                {
                   
string relId = node.Attributes["r:id"].Value;
                   
string sheetId = node.Attributes["sheetId"].Value;
                   
string name = node.Attributes["name"].Value;

                    XmlNode nodeSheets
= doc.DocumentElement.SelectSingleNode("d:sheets", nsManager);

                   
string relId1 = node.Attributes["r:id"].Value + "_" + fileNo.ToString();

                   
int maxSheetID = 0;
                   
int tempSheetID = 0;
                   
foreach (XmlNode note in nodeSheets.ChildNodes)
                    {
                        tempSheetID
= Convert.ToInt32(note.Attributes["sheetId"].Value);
                       
if (maxSheetID < tempSheetID)
                            maxSheetID
= tempSheetID;
                    }
                   
string sheetId1 = Convert.ToString(maxSheetID + 1);
                   
                    newSheetName
= name + "_" + fileNo.ToString();

                 
//构造更改所需内容,主要针对/xl/_rels/workbook.xml.rels文件
                    string sheetFileName;
                    Uri xmlUri
= new Uri(relFile, UriKind.Relative);
                    PackagePart xmlPart
= xlPackage.GetPart(xmlUri);
                    XmlDocument doc1
= new XmlDocument();
                    doc1.Load(xmlPart.GetStream());

                    XmlNode nodeSheet1
= SelectOneNode(doc1.DocumentElement.ChildNodes, "Id", relId);
                    sheetFileName
= nodeSheet1.Attributes["Target"].Value; //     [worksheets/sheetname.xml]
                    string sheetFileName1 = sheetFileName.Substring(sheetFileName.LastIndexOf('/') + 1, (sheetFileName.IndexOf('.') - sheetFileName.LastIndexOf('/') - 1)) + "_" + fileNo.ToString() + ".xml";

                   
string xmlString = "<Relationship Id=\"" + relId1 + "\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet\" Target=\"worksheets/" + sheetFileName1.ToLower() + "\" />";

                    XmlNode node1
= doc1.DocumentElement;
                    node1.InnerXml
+= xmlString;

                   
//拷贝sheet文件(若文件已存在,则抛出异常),并修改主关系文件[content_types].xml
                    string sheetXmlToPaste = "/xl/worksheets/" + sheetFileName1.ToLower();
                    CopyXmlFile(xlPackage,
"/xl/" + sheetFileName, sheetXmlToPaste);

                   
//修改workbook.xml
                    nodeSheets.InnerXml += "<sheet name=\"" + newSheetName + "\" sheetId=\"" + sheetId1 + "\" r:id=\"" + relId1 + "\" />";
                    doc.Save(documentPart.GetStream(FileMode.Create, FileAccess.Write));

                   
//修改/xl/_rels/workbook.xml.rels文件
                    doc1.Save(xmlPart.GetStream(FileMode.Create, FileAccess.Write));
                    xlPackage.Flush();

                    xlPackage.Close();
                   
return newSheetName;
                }
            }
           
catch
            {
                xlPackage.Close();
               
return newSheetName;
            }
        }

       
internal void CopyXmlFile(Package xlPackage, string sheetXmlToCopy, string sheetXmlToPaste)
        {
            Uri sheetUri
= new Uri(sheetXmlToCopy, UriKind.Relative);
            PackagePart sheetPart
= xlPackage.GetPart(sheetUri);
            XmlDocument doc
= new XmlDocument();
            doc.Load(sheetPart.GetStream());
            Uri xmlUri
= new Uri(sheetXmlToPaste, UriKind.Relative);
           
if (xlPackage.PartExists(xmlUri))
            {
                xlPackage.Close();
               
throw new InvalidOperationException("XML part is existing.");
            }
            PackagePart xmlPart
= xlPackage.CreatePart(xmlUri, @"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml");

           
using (Stream outputStream = xmlPart.GetStream(FileMode.Create, FileAccess.Write))
            {
               
using (StreamWriter writer = new StreamWriter(outputStream))
                {
                    writer.Write(doc.InnerXml);
                    writer.Close();
                }
            }
           
//修改主关系文件[content_types].xml
            string schemaRelationships = @"http://schemas.openxmlformats.org/officeDocument/2006/relationships";
            PackageRelationship rel
= xlPackage.CreateRelationship(xmlUri, TargetMode.Internal, schemaRelationships + "/worksheet");
            xlPackage.Flush();
        }
代码中CopySheet和CopyXMLFile为主要函数,负责完成表单的复制和对应XML文件的修改,后面还有三个辅助函数。

下面是辅助函数: 
     
public XmlNode SelectOneNode(XmlNodeList nodes, string name)
        {
            foreach (XmlNode n in nodes)
            {
                if (n.Name == name)
                    return n;
            }

            return null;
        }
        public XmlNode SelectOneNode(XmlNodeList nodes, string name, string value)
        {
            foreach (XmlNode n in nodes)
            {
                if (n.Attributes[name].Value == value)
                    return n;
            }

            return null;
        }
        internal void AddNode(Package xlPackage, string relFile, string xmlString, string singleNode)
        {
            Uri xmlUri = new Uri(relFile, UriKind.Relative);
            PackagePart xmlPart = xlPackage.GetPart(xmlUri);
            XmlDocument doc = new XmlDocument();
            doc.Load(xmlPart.GetStream());
            XmlNode node = doc.DocumentElement;
            node.InnerXml += xmlString;
            doc.Save(xmlPart.GetStream(FileMode.Create, FileAccess.Write));
            xlPackage.Flush();
        }
最后, 推荐大家几个好的地方,用来实现对Excel 2007文件的操作:
1、微软的MSDN,http://msdn2.microsoft.com/en-us/library/bb508943.aspx,上面有详细的讲解,而且还有很多例子下载;
2、国外已经有大侠完成了功能封装,而且开源的,参见:http://www.codeplex.com/ExcelPackage,当时我们也想直接使用这个包,但是唯独缺少我们最主要的功能:表单的复制,所以就自己研究,写了复制的方法;
3、如果大家有需求,我还会把微软的包做一下整理,然后进行简单封装;
4、除此之外,我们在本项目中还实现了数据的批量赋值(写好数据和excel中XML文件的映射文件)和在线转换低版本(Com实现),我会陆续整理。
【全文结束】
发表您的评论:
署名:记住我
主页:
内容: