c# merge openxml docx openxml-sdk

c# - Combinar varios documentos de Word en un Open Xml



merge openxml (3)

Tengo alrededor de 10 documentos de palabras que genero usando xml abierto y otras cosas. Ahora me gustaría crear otro documento de Word y uno por uno me gustaría unirlos a este documento recién creado. Deseo usar open xml, cualquier sugerencia sería apreciable. A continuación se muestra mi código:

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 { } }

Actualizar (usando 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(); }

¿Por qué este código sobrescribe el contenido del último archivo cuando uso varios archivos? Actualización 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()); } }

Este código está agregando los datos de Test2 dos veces, en lugar de los datos de Test1 también. Significa que obtengo

Test Test2 Test2

en lugar de :

Test Test1 Test2


Fácil de usar en 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(); } } }


Hay un bonito API de envoltorio (Document Builder 2.2) alrededor de un xml abierto especialmente diseñado para fusionar documentos, con la flexibilidad de elegir los párrafos para fusionar, etc. Puede descargarlo desde here (actualización: movido a github ).

La documentación y los modelos de pantalla sobre cómo usarla están here .

Actualización: Ejemplo de código

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");

Types Source y WmlDocument son de la API de Document Builder.

Incluso puede agregar las rutas de archivo directamente si elige como:

sources.Add(new Source(new WmlDocument(@"C:/FileToBeMerged1.docx")); sources.Add(new Source(new WmlDocument(@"C:/FileToBeMerged2.docx"));

Encontró esta agradable comparación entre los AltChunk y Document Builder para fusionar documentos, lo que AltChunk útil para elegir según los requisitos.

También puede usar la biblioteca DocX para combinar documentos, pero prefiero Document Builder a esto para combinar documentos.

Espero que esto ayude.


Usando solo openXML SDK, puedes usar el elemento AltChunk para combinar el documento múltiple en uno.

Este enlace, the-easy-way-to-assemble-multiple-word-documents y este Cómo utilizar altChunk para el ensamblaje de documentos, proporciona algunos ejemplos.

EDITAR 1

En base a su código que usa altchunk en la pregunta actualizada (actualización # 1) , aquí está el código VB.Net que he probado y que funciona como un encanto para mí:

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

Editar 2

El segundo problema (actualización # 2)

Este código está agregando los datos de Test2 dos veces, en lugar de los datos de Test1 también.

Está relacionado con altchunkid .

Para cada documento que desee fusionar en el documento principal, debe:

  1. agregue un AlternativeFormatImportPart en mainDocumentPart con un Id que debe ser único. Este elemento contiene los datos insertados.
  2. agregue en el cuerpo un elemento Altchunk en el que configure el id para que haga referencia al AlternativeFormatImportPart anterior.

En su código, está utilizando la misma Id. Para todos los AltChunks . Es por eso que ves muchas veces el mismo texto.

No estoy seguro de que el altchunkid sea único con su código: string altChunkId = "AltChunkId" + DateTime.Now.Ticks.ToString().Substring(0, 2);

Si no necesita establecer un valor específico, le recomiendo que no establezca explícitamente el AltChunkId cuando agregue el AlternativeFormatImportPart . En su lugar, obtienes uno generado por el SDK como este:

VB.Net

Dim chunk As AlternativeFormatImportPart = mainPart.AddAlternativeFormatImportPart(DocumentFormat.OpenXml.Packaging.AlternativeFormatImportPartType.WordprocessingML) Dim altchunkid As String = mainPart.GetIdOfPart(chunk)

DO#

AlternativeFormatImportPart chunk = mainPart.AddAlternativeFormatImportPart(DocumentFormat.OpenXml.Packaging.AlternativeFormatImportPartType.WordprocessingML); string altchunkid = mainPart.GetIdOfPart(chunk);