将多个word文档合并为一个Open Xml
Posted
技术标签:
【中文标题】将多个word文档合并为一个Open Xml【英文标题】:Merge multiple word documents into one Open Xml 【发布时间】:2013-08-23 11:52:16 【问题描述】:我有大约 10 个 word 文档,这些文档是使用 open xml 和其他东西生成的。 现在我想创建另一个word文档,我想将它们一个一个地加入到这个新创建的文档中。 我希望使用 open xml,任何提示都会很明显。 以下是我的代码:
private void CreateSampleWordDocument()
//string sourceFile = Path.Combine("D:\\GeneralLetter.dot");
//string destinationFile = Path.Combine("D:\\New.doc");
string sourceFile = Path.Combine("D:\\GeneralWelcomeLetter.docx");
string destinationFile = Path.Combine("D:\\New.docx");
try
// Create a copy of the template file and open the copy
//File.Copy(sourceFile, destinationFile, true);
using (WordprocessingDocument document = WordprocessingDocument.Open(destinationFile, true))
// Change the document type to Document
document.ChangeDocumentType(DocumentFormat.OpenXml.WordprocessingDocumentType.Document);
//Get the Main Part of the document
MainDocumentPart mainPart = document.MainDocumentPart;
mainPart.Document.Save();
catch
更新(使用 AltChunks):
using (WordprocessingDocument myDoc = WordprocessingDocument.Open("D:\\Test.docx", true))
string altChunkId = "AltChunkId" + DateTime.Now.Ticks.ToString().Substring(0, 2) ;
MainDocumentPart mainPart = myDoc.MainDocumentPart;
AlternativeFormatImportPart chunk = mainPart.AddAlternativeFormatImportPart(
AlternativeFormatImportPartType.WordprocessingML, altChunkId);
using (FileStream fileStream = File.Open("D:\\Test1.docx", FileMode.Open))
chunk.FeedData(fileStream);
AltChunk altChunk = new AltChunk();
altChunk.Id = altChunkId;
mainPart.Document
.Body
.InsertAfter(altChunk, mainPart.Document.Body.Elements<Paragraph>().Last());
mainPart.Document.Save();
为什么当我使用多个文件时,这段代码会覆盖最后一个文件的内容? 更新 2:
using (WordprocessingDocument myDoc = WordprocessingDocument.Open("D:\\Test.docx", true))
MainDocumentPart mainPart = myDoc.MainDocumentPart;
string altChunkId = "AltChunkId" + DateTime.Now.Ticks.ToString().Substring(0, 3);
AlternativeFormatImportPart chunk = mainPart.AddAlternativeFormatImportPart(AlternativeFormatImportPartType.WordprocessingML, altChunkId);
using (FileStream fileStream = File.Open("d:\\Test1.docx", FileMode.Open))
chunk.FeedData(fileStream);
AltChunk altChunk = new AltChunk();
altChunk.Id = altChunkId;
mainPart.Document
.Body
.InsertAfter(altChunk, mainPart.Document.Body
.Elements<Paragraph>().Last());
mainPart.Document.Save();
using (FileStream fileStream = File.Open("d:\\Test2.docx", FileMode.Open))
chunk.FeedData(fileStream);
AltChunk altChunk = new AltChunk();
altChunk.Id = altChunkId;
mainPart.Document
.Body
.InsertAfter(altChunk, mainPart.Document.Body
.Elements<Paragraph>().Last());
using (FileStream fileStream = File.Open("d:\\Test3.docx", FileMode.Open))
chunk.FeedData(fileStream);
AltChunk altChunk = new AltChunk();
altChunk.Id = altChunkId;
mainPart.Document
.Body
.InsertAfter(altChunk, mainPart.Document.Body
.Elements<Paragraph>().Last());
此代码附加了两次 Test2 数据,也代替了 Test1 数据。 意味着我得到:
Test
Test2
Test2
而不是:
Test
Test1
Test2
【问题讨论】:
就像 chirs 指出的那样,您对所有 AltChunk 使用相同的 Id。它们必须是唯一的。 好的,现在完成,感谢您一直对我的耐心。 我很高兴看到您终于解决了您的问题 :) 是的,它与 Altchunkid 有关。我已经编辑了我的答案,因为它可能不是很清楚。 【参考方案1】:仅使用openXML SDK,您可以使用AltChunk
元素将多个文档合并为一个。
此链接the-easy-way-to-assemble-multiple-word-documents 和此链接How to Use altChunk for Document Assembly 提供了一些示例。
编辑 1
根据您在更新后的问题中使用altchunk
的代码(update#1),这是我测试过的 VB.Net 代码,它对我来说就像一个魅力:
Using myDoc = DocumentFormat.OpenXml.Packaging.WordprocessingDocument.Open("D:\\Test.docx", True)
Dim altChunkId = "AltChunkId" + DateTime.Now.Ticks.ToString().Substring(0, 2)
Dim mainPart = myDoc.MainDocumentPart
Dim chunk = mainPart.AddAlternativeFormatImportPart(
DocumentFormat.OpenXml.Packaging.AlternativeFormatImportPartType.WordprocessingML, altChunkId)
Using fileStream As IO.FileStream = IO.File.Open("D:\\Test1.docx", IO.FileMode.Open)
chunk.FeedData(fileStream)
End Using
Dim altChunk = New DocumentFormat.OpenXml.Wordprocessing.AltChunk()
altChunk.Id = altChunkId
mainPart.Document.Body.InsertAfter(altChunk, mainPart.Document.Body.Elements(Of DocumentFormat.OpenXml.Wordprocessing.Paragraph).Last())
mainPart.Document.Save()
End Using
编辑 2
第二期(update#2)
此代码将 Test2 数据附加两次,以代替 Test1 数据 好吧。
与altchunkid
相关。
对于要合并到主文档中的每个文档,您需要:
-
在
mainDocumentPart
中添加AlternativeFormatImportPart
和Id
必须是唯一的。此元素包含插入的数据
在正文中添加一个Altchunk
元素,您可以在其中设置id
以引用之前的AlternativeFormatImportPart
。
在您的代码中,您对所有 AltChunks
使用相同的 ID。这就是为什么您会多次看到相同的文本。
我不确定 altchunkid 在您的代码中是否是唯一的:string altChunkId = "AltChunkId" + DateTime.Now.Ticks.ToString().Substring(0, 2);
如果您不需要设置特定值,我建议您在添加AlternativeFormatImportPart
时不要显式设置AltChunkId
。相反,您会得到一个由 SDK 生成的,如下所示:
VB.Net
Dim chunk As AlternativeFormatImportPart = mainPart.AddAlternativeFormatImportPart(DocumentFormat.OpenXml.Packaging.AlternativeFormatImportPartType.WordprocessingML)
Dim altchunkid As String = mainPart.GetIdOfPart(chunk)
C#
AlternativeFormatImportPart chunk = mainPart.AddAlternativeFormatImportPart(DocumentFormat.OpenXml.Packaging.AlternativeFormatImportPartType.WordprocessingML);
string altchunkid = mainPart.GetIdOfPart(chunk);
【讨论】:
这不是我想做的事,也不例外。我正在使用 Altchunks 发布我的更新代码。 我是否还需要在 docx 文件中做一些事情,比如添加书签类型的其他操作? @ItiTyagi 不,在我的测试中,我刚刚创建了两个带有简单文本的文件(Text1 和 Text2)。运行此代码后,文件 Test.docx 在我打开时包含两段。 你知道吗,我有一个开放的办公室,所以它没有反映,但是当我在办公室打开它时,它就起作用了。 合并多个时只覆盖最后一个【参考方案2】:围绕开放 xml 有一个很好的包装 API (Document Builder 2.2),专门用于合并文档,可以灵活地选择要合并的段落等。您可以从 here 下载它(更新:移至 github) .
有关如何使用它的文档和屏幕截图是here。
更新:代码示例
var sources = new List<Source>();
//Document Streams (File Streams) of the documents to be merged.
foreach (var stream in documentstreams)
var tempms = new MemoryStream();
stream.CopyTo(tempms);
sources.Add(new Source(new WmlDocument(stream.Length.ToString(), tempms), true));
var mergedDoc = DocumentBuilder.BuildDocument(sources);
mergedDoc.SaveAs(@"C:\TargetFilePath");
类型 Source
和 WmlDocument
来自 Document Builder API。
如果您选择 as,您甚至可以直接添加文件路径:
sources.Add(new Source(new WmlDocument(@"C:\FileToBeMerged1.docx"));
sources.Add(new Source(new WmlDocument(@"C:\FileToBeMerged2.docx"));
在AltChunk
和Document Builder
之间找到了这个Nice Comparison 合并文档的方法 - 有助于根据需求进行选择。
您也可以使用 DocX 库来合并文档,但我更喜欢 Document Builder 而不是它来合并文档。
希望这会有所帮助。
【讨论】:
有没有办法通过编码打开 xml,因为这个任务真的很吃我,我不能使用任何其他工具等。 这些库是围绕 OpenXml 的开源包装器。 Document Builder 使用 Open Xml sdk 进行合并,没有硬依赖。合并文档不是一项简单的任务,您必须在不丢失关系的情况下迁移样式 + 其他打开的 xml 部分的内容!当您在文档中有图片时,这将成为一场噩梦。 Document Builder Api 的源代码会给你一个相同的想法。 我只需要附加内容,作为一个页面,这样我就可以一次打印了。 恕我直言,最简单的方法是使用 Document Builder。我添加了代码 sn-p。请检查更新的答案。【参考方案3】:这些答案中唯一缺少的是for
循环。
对于那些只想复制/粘贴的人:
void MergeInNewFile(string resultFile, IList<string> filenames)
using (WordprocessingDocument document = WordprocessingDocument.Create(resultFile, WordprocessingDocumentType.Document))
MainDocumentPart mainPart = document.AddMainDocumentPart();
mainPart.Document = new Document(new Body());
foreach (string filename in filenames)
AlternativeFormatImportPart chunk = mainPart.AddAlternativeFormatImportPart(AlternativeFormatImportPartType.WordprocessingML);
string altChunkId = mainPart.GetIdOfPart(chunk);
using (FileStream fileStream = File.Open(filename, FileMode.Open))
chunk.FeedData(fileStream);
AltChunk altChunk = new AltChunk Id = altChunkId ;
mainPart.Document.Body.AppendChild(altChunk);
mainPart.Document.Save();
所有功劳归于 Chris 和 yonexbat
【讨论】:
【参考方案4】:在 C# 中易于使用:
using System;
using System.IO;
using System.Linq;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
namespace WordMergeProject
public class Program
private static void Main(string[] args)
byte[] word1 = File.ReadAllBytes(@"..\..\word1.docx");
byte[] word2 = File.ReadAllBytes(@"..\..\word2.docx");
byte[] result = Merge(word1, word2);
File.WriteAllBytes(@"..\..\word3.docx", result);
private static byte[] Merge(byte[] dest, byte[] src)
string altChunkId = "AltChunkId" + DateTime.Now.Ticks.ToString();
var memoryStreamDest = new MemoryStream();
memoryStreamDest.Write(dest, 0, dest.Length);
memoryStreamDest.Seek(0, SeekOrigin.Begin);
var memoryStreamSrc = new MemoryStream(src);
using (WordprocessingDocument doc = WordprocessingDocument.Open(memoryStreamDest, true))
MainDocumentPart mainPart = doc.MainDocumentPart;
AlternativeFormatImportPart altPart =
mainPart.AddAlternativeFormatImportPart(AlternativeFormatImportPartType.WordprocessingML, altChunkId);
altPart.FeedData(memoryStreamSrc);
var altChunk = new AltChunk();
altChunk.Id = altChunkId;
OpenXmlElement lastElem = mainPart.Document.Body.Elements<AltChunk>().LastOrDefault();
if(lastElem == null)
lastElem = mainPart.Document.Body.Elements<Paragraph>().Last();
//Page Brake einfügen
Paragraph pageBreakP = new Paragraph();
Run pageBreakR = new Run();
Break pageBreakBr = new Break() Type = BreakValues.Page ;
pageBreakP.Append(pageBreakR);
pageBreakR.Append(pageBreakBr);
return memoryStreamDest.ToArray();
【讨论】:
此答案的代码中缺少某些内容。 你在用 lastElem 做什么?它似乎已设置但未使用。 这个方法不行。只有第一个文档被添加到输出中。以上是关于将多个word文档合并为一个Open Xml的主要内容,如果未能解决你的问题,请参考以下文章