现在的位置: 首页 > 综合 > 正文

asp.net使用openXML生成OfficeWord2007

2013年12月06日 ⁄ 综合 ⁄ 共 12967字 ⁄ 字号 评论关闭

最近工作中在开发的一个项目需要根据数据库的数据来动态生成Word文档存放在服务端,让用户随时随地下载。以前没有接触过Office开发,临时查了些资料,最终用COM接口完成了。项目虽然是完成了,不过我还是对期间处理Word COM接口的繁琐过程心有余悸。后期发现存在遗留问题,而且效率也不高,生成上万条的数据的时候实在是让人无语。跟朋友聊过类似的问题后,朋友提出可以用OpenXML。稍微了解一下OpenXML,于是我就动了用OpenXML的念头,稍稍的研究了一下,果然速度和效率不是吹的。

以下是我整理的一些在项目中用到过的方法。

项目中的功能是根据主模版文档来生成word,还需从子模板中复制内容到word中。总结了一下,分成四部分来操作。(实际上后三部分才使用到Openxml)

注意:(源代码管理器中开发的所有文件都会为“只读”。)需要操作的文档都必须把“只读”属性去掉,最好是把存放模板文件的文件夹整体去掉“只读”属性。

第一部,当然是最简单的,复制模板到指定位置,操作复制后的文件。

  if (File.Exists(filename)) //判断文件已存在,则删除

            {
                File.Delete(filename);
            }
            File.Copy(objTemplate, filename, true);   //先复制一份模板文件

第二部分,复制子模板内容到Word文档中某个位置,位置用书签来定位。要考滤到需要复制的文档中存在一些什么元素,都需要相对应复制过来,并要考滤相关联的xml文件也需改变。此示例中的子模板存在以下元素:项目符号:WNumberingId;段落样式:ParagraphStyleId;超链接:Hyperlink;块样式:RunStyle

以下是复制子模板的内容:

        /// <summary>
        /// 复制模板内容到Word中书签的指定位置
         ///=========================================
        /// </summary>
        /// <param name="temps">多个子模板</param>
        /// <param name="filePath">To文档径</param>
        /// <param name="bookmarkName">书签名称</param>
        public static void CopyTempsToFile(string[] temps, string filePath, string bookmarkName)
        {
            using (WordprocessingDocument objectiveDoc = WordprocessingDocument.Open(filePath, true))
            {
                try
                {
                    MainDocumentPart mainPart = objectiveDoc.MainDocumentPart;
                    int s = mainPart.ThemePart.ImageParts.Count();
                    #region
                    var res = from t in objectiveDoc.MainDocumentPart.Document.Body.Descendants<WBookmarkStart>()
                              where t.Name == bookmarkName
                              select t;
                    var bookmark = res.SingleOrDefault();
                    if (bookmark != null)
                    {
                        var parent = bookmark.Parent.NextSibling<WParagraph>();
                        foreach (WText txt in bookmark.Parent.Descendants<WText>())
                        {
                            txt.Text = "";
                        }
                        foreach (var name in temps)
                        {
                            string filename = name;
                            System.IO.FileInfo info = new System.IO.FileInfo(filename);
                            if (info.IsReadOnly)
                            {
                                throw new ApplicationException("文件:" + name + "为只读,请修改后再执行操作。");
                            }
                            try
                            {
                                using (WordprocessingDocument sourceDoc = WordprocessingDocument.Open(filename, true))
                                {
                                    Hashtable htStyles = CreateStylesPart(sourceDoc, objectiveDoc);
                                    Hashtable htNumberingInstance = CreateNumberingPart(sourceDoc, objectiveDoc);
                                    Hashtable htHyperlink = CreateHyperlinkPart(sourceDoc, objectiveDoc);
                                    //复制段落到书签

                                    WBody body = sourceDoc.MainDocumentPart.Document.Body;
                                    int p = 0;
                                    int tb = 0;
                                    var ps = from t in body.ChildElements select t;
                                    foreach (var item in ps)
                                    {
                                        if (item.GetType().Name == "Paragraph")
                                        {
                                            WParagraph p1 = body.Elements<WParagraph>().ElementAt(p);
                                            OpenXmlElement oxePara = p1.CloneNode(true);
                                            if (oxePara.Descendants<WNumberingId>().Count() > 0)
                                            {
                                                int numberID = oxePara.Descendants<WNumberingId>().First().Val;
                                                if (htNumberingInstance[numberID] != null)
                                                {
                                                    oxePara.Descendants<WNumberingId>().First().Val = WebHelper.SafeParse(htNumberingInstance[numberID], 0);
                                                }
                                            }
                                            if (oxePara.Descendants<ParagraphStyleId>().Count() > 0)
                                            {
                                                string styleId = oxePara.Descendants<ParagraphStyleId>().First().Val;
                                                if (htStyles[styleId] != null)
                                                {
                                                    oxePara.Descendants<ParagraphStyleId>().First().Val = htStyles[styleId].ToString();
                                                }
                                            }
                                            if (oxePara.Descendants<Hyperlink>().Count() > 0)
                                            {
                                                string hlnkId = oxePara.Descendants<Hyperlink>().First().Id;
                                                if (htHyperlink[hlnkId] != null)
                                                {
                                                    oxePara.Descendants<Hyperlink>().First().Id = htHyperlink[hlnkId].ToString();
                                                }
                                            }
                                            if (oxePara.Descendants<RunStyle>().Count() > 0)
                                            {
                                                string rstyId = oxePara.Descendants<RunStyle>().First().Val;
                                                if (htStyles[rstyId] != null)
                                                {
                                                    oxePara.Descendants<RunStyle>().First().Val = htStyles[rstyId].ToString();
                                                }
                                            }
                                            parent.InsertBeforeSelf(oxePara);
                                            p++;
                                        }
                                        if (item.GetType().Name == "Table")
                                        {
                                            WTable tb1 = body.Elements<WTable>().ElementAt(tb);
                                            OpenXmlElement oxeTab = tb1.CloneNode(true);
                                            if (oxeTab.Descendants<WNumberingId>().Count() > 0)
                                            {
                                                foreach (var oexItem in oxeTab.Descendants<WNumberingId>())
                                                {
                                                    int oldId = oexItem.Val;
                                                    if (htNumberingInstance[oldId] != null)
                                                    {
                                                        oexItem.Val = WebHelper.SafeParse(htNumberingInstance[oldId], 0);
                                                    }
                                                }
                                            }
                                            parent.InsertBeforeSelf(oxeTab);
                                            tb++;
                                        }
                                    }
                                }
                            }
                            catch (Exception ex)
                            {
                                throw new ApplicationException("出错文件:" + name + ",错误信息:" + ex.Message.ToString());
                            }
                        }
                    }
                    #endregion
                }
                catch (Exception)
                {
                    throw;
                }
                finally
                {
                    objectiveDoc.Close();
                    objectiveDoc.Dispose();
                }
            }
        }

生成word样式的方法:

/// <summary>
/// 创建子模板的样式
///========================================= 
/// </summary>
/// <param name="sourceDoc">来源文档</param>
/// <param name="objectiveDoc">目标文档</param>
/// <returns></returns>
private static Hashtable CreateStylesPart(WordprocessingDocument sourceDoc, WordprocessingDocument objectiveDoc)
        {
            try
            {
                MainDocumentPart mainPart = objectiveDoc.MainDocumentPart;  //目标文档主体
                StyleDefinitionsPart stPart = mainPart.StyleDefinitionsPart;
                Styles styles2 = null;
                if (stPart != null)
                {
                    styles2 = stPart.Styles;
                }
                else
                {
                    int st = mainPart.Parts.Count();
                    styles2 = new Styles();
                    styles2.AddNamespaceDeclaration("r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships");
                    styles2.AddNamespaceDeclaration("w", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
                    DocDefaults docDefaults2 = GenerateDocDefaults();
                    LatentStyles latentStyles2 = GenerateLatentStyles();
                }
                Hashtable htStyles = new Hashtable();
                if (sourceDoc.MainDocumentPart.StyleDefinitionsPart != null)
                {
                    #region 为目标文档添加 子模板中的 样式
                    int styleNum = 0;
                    OpenXmlElement oxeStyle = sourceDoc.MainDocumentPart.StyleDefinitionsPart.Styles.CloneNode(true);
                    foreach (var item in oxeStyle.Descendants<Style>())
                    {
                        #region 检查新的 styleId
                        string styleId = "a" + styleNum;
                        foreach (var objStyle in styles2.Descendants<Style>())
                        {
                            if (objStyle.StyleId == styleId)
                            {
                                styleNum++;
                                styleId = "a" + styleNum;
                            }
                        }
                        #endregion

                        string sourId = item.StyleId;
                        htStyles.Add(sourId, styleId);
                        item.StyleId = styleId;
                        int styNum = styles2.Elements<Style>().Count();
                        if (styNum > 0)
                        {
                            styles2.Elements<Style>().ElementAt(styNum - 1).InsertAfterSelf(item.CloneNode(true)); //下标从0开始
                        }
                        else
                        {
                            styles2.ChildElements[1].Append(item);  //第一个Style  节点
                        }
                        styleNum++;
                    }
                    if (stPart == null)
                    {
                        stPart.Styles = styles2;
                        stPart.Styles.Append(sourceDoc.MainDocumentPart.StyleDefinitionsPart.Styles.CloneNode(true));
                    }
                    #endregion
                }
                return htStyles;
            }
            catch (Exception ex)
            {
                throw new ApplicationException("读取子模板样式时出错,错误信息:" + ex.Message.ToString());
            }
        }

生成项目符号的方法:

/// <summary>
/// 创建子模板的项目符号
///========================================= 
/// </summary>
/// <param name="sourceDoc">来源文档</param>
/// <param name="objectiveDoc">目标文档</param>
/// <returns></returns>
private static Hashtable CreateNumberingPart(WordprocessingDocument sourceDoc, WordprocessingDocument objectiveDoc)
        {
            try
            {

                MainDocumentPart mainPart = objectiveDoc.MainDocumentPart;
                WNumbering numbering2 = null;
                NumberingDefinitionsPart ndPart = mainPart.NumberingDefinitionsPart;
                if (ndPart != null)
                {
                    numbering2 = ndPart.Numbering;
                }
                else
                {
                    int c = mainPart.Parts.Count();
                    ndPart = mainPart.AddNewPart<NumberingDefinitionsPart>("rId" + c + 1);
                    numbering2 = new WNumbering();
                    numbering2.AddNamespaceDeclaration("ve", "http://schemas.openxmlformats.org/markup-compatibility/2006");
                    numbering2.AddNamespaceDeclaration("o", "urn:schemas-microsoft-com:office:office");
                    numbering2.AddNamespaceDeclaration("r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships");
                    numbering2.AddNamespaceDeclaration("m", "http://schemas.openxmlformats.org/officeDocument/2006/math");
                    numbering2.AddNamespaceDeclaration("v", "urn:schemas-microsoft-com:vml");
                    numbering2.AddNamespaceDeclaration("wp", "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing");
                    numbering2.AddNamespaceDeclaration("w10", "urn:schemas-microsoft-com:office:word");
                    numbering2.AddNamespaceDeclaration("w", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
                    numbering2.AddNamespaceDeclaration("wne", "http://schemas.microsoft.com/office/word/2006/wordml");
                }
                Hashtable htNumberingInstance = new Hashtable();
                Hashtable htAbsNum = new Hashtable();
                if (sourceDoc.MainDocumentPart.NumberingDefinitionsPart != null)
                {
                    #region 新增项目符号
                    var numbering1 = from n in sourceDoc.MainDocumentPart.NumberingDefinitionsPart.Numbering.Elements() select n;
                    int npbId = 0;
                    int anId = 0;
                    int niId = 1;
                    int oldAbsId = 0;
                    if (ndPart != null)
                    {
                        npbId = numbering2.Elements<WNumberingPictureBullet>().Count();
                        anId = numbering2.Elements<WAbstractNum>().Count();
                        niId = numbering2.Elements<WNumberingInstance>().Count() + 1;
                        oldAbsId = numbering2.Descendants<WAbstractNumId>().Count();    //主模板原有的 WAbstractNumId数量
                    }
                    foreach (var number in numbering1)
                    {
                        if (number.GetType().Name == "NumberingPictureBullet")
                        {
                            WNumberingPictureBullet numberingPictureBullet1 = new WNumberingPictureBullet() { NumberingPictureBulletId = npbId };
                            foreach (var pic in number.ChildElements)
                            {
                                numberingPictureBullet1.Append(pic.CloneNode(true));
                            }
                            int picNum = numbering2.Elements<WNumberingPictureBullet>().Count();
                            if (picNum > 0)
                            {
                                numbering2.Elements<WNumberingPictureBullet>().ElementAt(picNum - 1).InsertAfterSelf(numberingPictureBullet1); //下标从0开始

                            }
                            else
                            {
                                numbering2.ChildElements[0].InsertBeforeSelf(numberingPictureBullet1);  //第一个 NumberingPictureBullet 节点
                            }
                            npbId++;
                        }
                        if (number.GetType().Name == "AbstractNum")
                        {
                            WAbstractNum abstractNum1 = new WAbstractNum() { AbstractNumberId = anId };
                            foreach (var abs in number.ChildElements)
                            {
                                abstractNum1.Append(abs.CloneNode(true));
                            }
                            numbering2.Elements<WAbstractNum>().ElementAt(anId - 1).InsertAfterSelf(abstractNum1);
                            int oldID = WebHelper.SafeParse(GetXmlNodeId(number.OuterXml, "w:abstractNumId"), 0);
                            htAbsNum.Add(oldID, anId);
                            anId++;
                        }
                        if (number.GetType().Name == "NumberingInstance")
                        {
                            string strNumberingInstance = number.OuterXml;
                            int oldID = WebHelper.SafeParse(GetXmlNodeId(strNumberingInstance, "w:numId"), 0);
                            int newID = niId;
                            htNumberingInstance.Add(oldID, newID);
                            WNumberingInstance numberingInstance1 = new WNumberingInstance() { NumberID = niId };
                            foreach (var item in number.ChildElements)
                            {
                                if (item.GetType().Name == "AbstractNumId")
                                {
                                    int absId = WebHelper.SafeParse(GetXmlNodeId(item.OuterXml, "w:val"), 0);
                                    WAbstractNumId abstractNumId1 = new WAbstractNumId() { Val = WebHelper.SafeParse(htAbsNum[absId], 0) };
                                    numberingInstance1.Append(abstractNumId1);
                                }
                                else
                                {
                                    numberingInstance1.Append(item.CloneNode(true));
                                }
                            }
                            numbering2.Elements<WNumberingInstance>().ElementAt(niId - 2).InsertAfterSelf(numberingInstance1);
                            niId++;
                        }
                    }
                    if (ndPart == null)
                    {
                        ndPart.Numbering = numbering2;
                        ndPart.Numbering.Append(sourceDoc.MainDocumentPart.NumberingDefinitionsPart.Numbering.CloneNode(true));
                    }
                    #endregion
                }
                return htNumberingInstance;

            }
            catch (Exception ex)
            {
                throw new ApplicationException("读取子模板项目符号时出错,错误信息:" + ex.Message.ToString());
            }
        }

生成超链接的方法:

 
#region
/// <summary>
/// 创建子模板的超链接到父模板
///========================================= 
/// </summary>
/// <param name="sourceDoc">来源文档</param>
/// <param name="objectiveDoc">目标文档</param>
/// <returns></returns>
 private static Hashtable CreateHyperlinkPart(WordprocessingDocument sourceDoc, WordprocessingDocument objectiveDoc)
        {
            try
            {
                Hashtable ht = new Hashtable();
                MainDocumentPart mainPart = objectiveDoc.MainDocumentPart;  //目标文档主体
                int c = mainPart.Parts.Count();
                int hylnkNum = c + 1;
                if (sourceDoc.MainDocumentPart.HyperlinkRelationships != null)
                {
                    foreach (var item in sourceDoc.MainDocumentPart.HyperlinkRelationships)
                    {
                        string hylnkId = "rId" + hylnkNum;
                        foreach (var objHylnk in mainPart.Document.Descendants<Hyperlink>())
                        {
                            if (objHylnk.Id == hylnkId)
                            {
                                hylnkId = "rId" + hylnkNum + 1;
                            }
                        }
                        ht.Add(item.Id, hylnkId);
                        string url = item.Uri.ToString();
                        mainPart.AddHyperlinkRelationship(new System.Uri(url, System.UriKind.Absolute), true, hylnkId);
                        hylnkNum++;
                    }
                }
                return ht;
            }
            catch (Exception ex)
            {
                throw new ApplicationException("读取子模板超链时出错,错误信息:" + ex.Message.ToString());
            }
        }
#endregion

第三部分:替换word中的部分内容,此处需用到word中的书签功能。首先在word模板中定义好需要替换的书签,第一部复制模板的时候会自动复制过去。然后在程序中定义到要替换的内容,进行替换。

string[] tableremarks = new string[] 
                        { 
                            "name","age","sex"
                        };
                 string[]tablevalues = new string[] 
                        {
                            "tracy","24","girl"
                        };
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(filepath, true))
            {
                try
                {
                    for (int oIndex = 0; oIndex < tableremarks.Length; oIndex++)
                    {
                        string obDD_Name = tableremarks[oIndex];
                        string val = tablevalues[oIndex].ToString();
                        var res = from t in wordDoc.MainDocumentPart.Document.Body.Descendants<WBookmarkStart>()
                                  where t.Name == obDD_Name
                                  select t;
                        var bookmark = res.SingleOrDefault();
                        var pare = bookmark.Parent;
                        if (bookmark != null)
                        {
                            foreach (WText text in bookmark.NextSibling().Elements<WText>())
                            {
                                text.Text = val;
                            }
                        }
                    }
                }
                catch (Exception)
                {
                    throw;
                }
                finally
                {
                    wordDoc.Close();
                    wordDoc.Dispose();
                }
            }

第四部分:为word中的表格插入数据。

        /// <summary>
        /// 根据下标找到表格位置并添加数据
         ///=========================================
        /// </summary>
        /// <param name="filepath">文件路径</param>
        /// <param name="dt">需插入的数据,用DataTable接收</param>
        /// <param name="index">表格的下标(下标从0开始)</param>
        /// <param name="rowIndex">在表格的第几行开始添加数据(下标从0开始)</param>
        public static void AddDataToTableByIndex(string filepath, int tableIndex, DataTable dt, int rowIndex)
        {
            using (WordprocessingDocument doc = WordprocessingDocument.Open(filepath, true))
            {
                try
                {
                    WTable table = doc.MainDocumentPart.Document.Body.Elements<WTable>().ElementAt(tableIndex);
                    WTableRow row = table.Elements<WTableRow>().ElementAt(rowIndex);    //从第几行开始插入数据
                    #region 循环为表格添加数据
                    for (int i = 0; i < dt.Rows.Count; i++)
                    {
                        //添加行,复制第一行的元素,以免格式丢失
                        if (i > 0)
                        {
                            List<OpenXmlElement> rowElements = new List<OpenXmlElement>();
                            foreach (var item in row.Elements())
                            {
                                rowElements.Add(item.Clone() as OpenXmlElement);
                            }
                            WTableRow newRow = table.InsertAfter(new WTableRow(), row);
                            newRow.Append(rowElements);
                        }
                        //循环添加数据
                        for (int j = 0; j < dt.Columns.Count; j++)
                        {
                            WTableCell cell = row.Elements<WTableCell>().ElementAt(j);
                            AddTextToTableCell(cell, dt.Rows[i][j].ToString());
                        }
                    }
                    #endregion
                }
                catch (Exception)
                {
                    throw;
                }
                finally
                {
                    doc.Close();
                    doc.Dispose();
                }
            }
        }

PS:插入的数据太大,有可能会出现异常,小数据量没有问题。

 拒绝访问。(Exception FROM HRESULT:0x80070005(E_ACCESSDENIED))

我在项目中就出现过,找了很久没有找到原因,最后的解决方案是:

在C:\Documents and Settings\Default User\Local Settings\Application Data 位置下,建一个名为 IsolatedStorage的文件夹。

原理还没有搞清楚,个人认为,可能是因为数据量太大,需要一个虚拟存储空间吧。

Openxml很强大,需要研究的东西还很多,下面是几个比较详细的有关Openxml 的文章。

http://www.cnblogs.com/brooks-dotnet/archive/2010/02/08/1665600.html

http://blog.csdn.net/atian15/article/details/6948602

http://blog.csdn.net/april_zheng/article/details/6741143
 

 

【上篇】
【下篇】

抱歉!评论已关闭.