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:
- agregue un
AlternativeFormatImportPart
enmainDocumentPart
con unId
que debe ser único. Este elemento contiene los datos insertados. - agregue en el cuerpo un elemento
Altchunk
en el que configure elid
para que haga referencia alAlternativeFormatImportPart
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);