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

如何在 C# 中将 .docx 转换为 .pdf [关闭]

使用 OpenXML 和 Regex 在 Word Docx 中查找和替换撇号(')的问题

C# dotnet 使用 OpenXml 关闭时不自动保存文档方法

如何使用openxml C#在word文档中添加形状?

Office Open XML文档怎么转换成word文档 2003的