C# 使用 OpenXML 复制 DOCX 文件
Posted
技术标签:
【中文标题】C# 使用 OpenXML 复制 DOCX 文件【英文标题】:C# Duplicate DOCX File Using OpenXML 【发布时间】:2013-04-10 18:28:46 【问题描述】:我正在尝试复制 docx 文件内容并使用 C# 中的 OpenXML 将它们保存在同一个文件中
代码如下:
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(wordFileNamePath, true))
foreach(OpenXmlElement element in wordDoc.MainDocumentPart.Document.ChildElements)
OpenXmlElement cloneElement = (OpenXmlElement)element.Clone();
wordDoc.MainDocumentPart.Document.Append(cloneElement);
wordDoc.MainDocumentPart.Document.Save();
代码运行良好,可以满足我的需要。我的问题是生成的 docx 文件已部分损坏。当我打开我的文件时,我收到以下两条消息:
单击“确定”然后单击“是”将正常打开文件。但是,该文件一直被损坏,直到我“另存为”它(使用相同或不同的名称)。新保存的文件就是这样固定的。
通过使用 Open XML SDK 2.5 Productivity Tool for Microsoft Office,我可以验证文件并查看反映的代码。验证文件会出现以下 5 个错误:
所以我认为我在代码中使用的“克隆”功能会按原样复制元素,因此当它附加到文档时,会发生一些 ID 重复。
有没有办法在复制自身后获得一个正常工作的 DOCX 文件?任何替代代码表示赞赏。
【问题讨论】:
【参考方案1】:您的方法的问题在于它创建了无效的 Open XML 标记。这就是原因。
假设您有一个由以下标记表示的非常简单的 Word 文档:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:body>
<w:p>
<w:r>
<w:t>First paragraph</w:t>
</w:r>
</w:p>
<w:p>
<w:r>
<w:t>Second paragraph</w:t>
</w:r>
</w:p>
<w:body>
<w:document>
在您的 foreach
循环中,wordDoc.MainDocumentPart.Document.ChildElements
将是一个仅包含 w:body
元素的单元素列表。因此,您创建了w:body
元素的深层克隆并将其附加到w:document
。生成的 Open XML 标记如下所示:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:body>
<w:p>
<w:r>
<w:t>First paragraph</w:t>
</w:r>
</w:p>
<w:p>
<w:r>
<w:t>Second paragraph</w:t>
</w:r>
</w:p>
<w:body>
<w:body>
<w:p>
<w:r>
<w:t>First paragraph</w:t>
</w:r>
</w:p>
<w:p>
<w:r>
<w:t>Second paragraph</w:t>
</w:r>
</w:p>
<w:body>
<w:document>
以上是带有两个w:body
子元素的w:document
,这是无效的Open XML 标记,因为w:document
必须只有一个w:body
子元素。因此,Word 会显示该错误消息。
要解决此问题,您需要使用Document.Body
,只要您使用Document
。以下简化的示例显示了如何执行此操作。
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(wordFileNamePath, true))
Body body = wordDoc.MainDocumentPart.Document.Body;
IEnumerable<OpenXmlElement> clonedElements = body
.Elements()
.Select(e => e.CloneNode(true))
.ToList();
body.Append(clonedElements);
您会看到我没有显式保存Document
,因为using
语句和默认情况下自动保存这些文档的事实没有必要这样做。其次,我使用ToList()
在追加之前实现集合。这是为了避免在枚举同时更改的元素时出现任何问题。
【讨论】:
【参考方案2】:为什么不会被破坏?您正在打开一个文档,获取所有子元素,并将它们写入同一个文档。我不确定那应该做什么。
【讨论】:
是的,假设您有图像、文本和任何元素。我希望它们会被重复(附加)到文档中。 @yazanpro,那么你应该只从正文中获取元素。类似:wordDoc.MainDocumentPart.Document.Body.ChildElements
。并将它们附加到正文wordDoc.MainDocumentPart.Document.Body.Append(cloneElement);
您的意思是:使用 (WordprocessingDocument wordDoc = WordprocessingDocument.Open(wordFileNamePath, true)) foreach(wordDoc.MainDocumentPart.Document.Body.ChildElements 中的 OpenXmlElement 元素) OpenXmlElement cloneElement = (OpenXmlElement)element。克隆(); wordDoc.MainDocumentPart.Document.Body.Append(cloneElement); wordDoc.MainDocumentPart.Document.Save(); 我遇到了同样的问题
我的意思是在元素中具有重复 ID 的完全相同的结果文件
@Yazanpro,有点离题,但你为什么要这样做?以上是关于C# 使用 OpenXML 复制 DOCX 文件的主要内容,如果未能解决你的问题,请参考以下文章
使用 OpenXML 和 MemoryStream 在 ASP.NET 程序中编辑和保存 .docx
使用 OpenXML 和 Regex 在 Word Docx 中查找和替换撇号(')的问题