Necesito usar C# a través de programación para anexar varios preexistentes DOCX archivos en una sola, larga archivo DOCX - incluyendo especiales de marcas como balas y las imágenes. Encabezado y pie de página de la información se despojó de fuera, así que los que no van a estar para causar problemas.
Puedo encontrar un montón de información acerca de la manipulación de un individuo archivo DOCX .NET Framework 3, pero nada fácil ni obvia acerca de cómo combinar los archivos. También hay un programa de terceros (Acronis.Palabras) que lo va a hacer, pero es prohibitivamente caro.
[editar] la Automatización a través de la Palabra se ha sugerido, pero mi código se va a ejecutar en ASP.NET en un servidor web de IIS, por lo que salir a la Palabra no es una opción para mí. Lo siento por no mencionar que en el primer lugar. [/edit]
Gracias por cualquier ayuda!
Respuestas
¿Demasiados anuncios?A pesar de las buenas sugerencias y soluciones presentadas, desarrollé una alternativa. En mi opinión debe evitar el uso de palabra en aplicaciones de servidor completamente. Así que trabajé con OpenXML, pero no funcionó con AltChunk. He añadido texto al cuerpo original, recibo una lista de bytes [] en su lugar una lista de nombres de archivo pero usted puede cambiar fácilmente el código a sus necesidades.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Xml.Linq;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
namespace OfficeMergeControl
{
public class CombineDocs
{
public byte[] OpenAndCombine( IList<byte[]> documents )
{
MemoryStream mainStream = new MemoryStream();
mainStream.Write(documents[0], 0, documents[0].Length);
mainStream.Position = 0;
int pointer = 1;
byte[] ret;
try
{
using (WordprocessingDocument mainDocument = WordprocessingDocument.Open(mainStream, true))
{
XElement newBody = XElement.Parse(mainDocument.MainDocumentPart.Document.Body.OuterXml);
for (pointer = 1; pointer < documents.Count; pointer++)
{
WordprocessingDocument tempDocument = WordprocessingDocument.Open(new MemoryStream(documents[pointer]), true);
XElement tempBody = XElement.Parse(tempDocument.MainDocumentPart.Document.Body.OuterXml);
newBody.Add(tempBody);
mainDocument.MainDocumentPart.Document.Body = new Body(newBody.ToString());
mainDocument.MainDocumentPart.Document.Save();
mainDocument.Package.Flush();
}
}
}
catch (OpenXmlPackageException oxmle)
{
throw new OfficeMergeControlException(string.Format(CultureInfo.CurrentCulture, "Error while merging files. Document index {0}", pointer), oxmle);
}
catch (Exception e)
{
throw new OfficeMergeControlException(string.Format(CultureInfo.CurrentCulture, "Error while merging files. Document index {0}", pointer), e);
}
finally
{
ret = mainStream.ToArray();
mainStream.Close();
mainStream.Dispose();
}
return (ret);
}
}
}
Espero que esto le ayuda.
No necesita utilizar automatización. Archivos DOCX se basan en los formatos de la OpenXML. Son sólo los archivos zip con un montón de XML y partes binarias (archivos piensas) dentro. Puede abrir con la API de empaquetado (System.IO.Packaging en WindowsBase.dll) y manipularlos con cualquiera de las clases XML en el marco.
Echale OpenXMLDeveloper.org para más detalles.
Este es un muy tarde a la pregunta original y muy poco ha cambiado, pero pensé que me gustaría compartir de la manera que he escrito mi combinación de la lógica. Esto hace que el uso de XML Abierto de Herramientas de Poder
public byte[] CreateDocument(IList<byte[]> documentsToMerge)
{
List<Source> documentBuilderSources = new List<Source>();
foreach (byte[] documentByteArray in documentsToMerge)
{
documentBuilderSources.Add(new Source(new WmlDocument(string.Empty, documentByteArray), false));
}
WmlDocument mergedDocument = DocumentBuilder.BuildDocument(documentBuilderSources);
return mergedDocument.DocumentByteArray;
}
Actualmente está trabajando muy bien en nuestra aplicación. He cambiado el código un poco porque mis requisitos es que cada uno de los documentos que necesita ser procesada en primer lugar. Entonces, ¿qué pasa en un DTO objeto con la plantilla de la matriz de bytes y los diferentes valores que necesitan ser reemplazados. Aquí es cómo mi código en la actualidad se ve. Que toma el código un poco más.
public byte[] CreateDocument(IList<DocumentSection> documentTemplates)
{
List<Source> documentBuilderSources = new List<Source>();
foreach (DocumentSection documentTemplate in documentTemplates.OrderBy(dt => dt.Rank))
{
// Take the template replace the items and then push it into the chunk
using (MemoryStream templateStream = new MemoryStream())
{
templateStream.Write(documentTemplate.Template, 0, documentTemplate.Template.Length);
this.ProcessOpenXMLDocument(templateStream, documentTemplate.Fields);
documentBuilderSources.Add(new Source(new WmlDocument(string.Empty, templateStream.ToArray()), false));
}
}
WmlDocument mergedDocument = DocumentBuilder.BuildDocument(documentBuilderSources);
return mergedDocument.DocumentByteArray;
}
Desea usar AltChunks y la 1.0 de la SDK de OpenXml (un 2.0 mínimo, si se puede). Visita el blog de Eric White para más detalles y sólo como un gran recurso!. Este es un ejemplo de código que debe comenzar, si no funciona inmediatamente.
public void AddAltChunkPart(Stream parentStream, Stream altStream, string altChunkId)
{
//make sure we are at the start of the stream
parentStream.Position = 0;
altStream.Position = 0;
//push the parentStream into a WordProcessing Document
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(parentStream, true))
{
//get the main document part
MainDocumentPart mainPart = wordDoc.MainDocumentPart;
//create an altChunk part by adding a part to the main document part
AlternativeFormatImportPart chunk = mainPart.AddAlternativeFormatImportPart(altChunkPartType, altChunkId);
//feed the altChunk stream into the chunk part
chunk.FeedData(altStream);
//create and XElement to represent the new chunk in the document
XElement newChunk = new XElement(altChunk, new XAttribute(relId, altChunkId));
//Add the chunk to the end of the document (search to last paragraph in body and add at the end)
wordDoc.MainDocumentPart.GetXDocument().Root.Element(body).Elements(paragraph).Last().AddAfterSelf(newChunk);
//Finally, save the document
wordDoc.MainDocumentPart.PutXDocument();
}
//reset position of parent stream
parentStream.Position = 0;
}
Escribí una aplicación de prueba de poco hace un tiempo para ello. Mi aplicación de prueba se trabajó con documentos de Word 2003 (.doc) no .docx, pero imagino que el proceso es el mismo - creo que debes cambiar es utilizar una versión más reciente del ensamblado de interoperabilidad primario. Este código sería mucho más con las nuevas características de C# 4.0...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Office.Interop.Word;
using Microsoft.Office.Core;
using System.Runtime.InteropServices;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
new Program().Start();
}
private void Start()
{
object fileName = Path.Combine(Environment.CurrentDirectory, @"NewDocument.doc");
File.Delete(fileName.ToString());
try
{
WordApplication = new ApplicationClass();
var doc = WordApplication.Documents.Add(ref missing, ref missing, ref missing, ref missing);
try
{
doc.Activate();
AddDocument(@"D:\Projects\WordTests\ConsoleApplication1\Documents\Doc1.doc", doc, false);
AddDocument(@"D:\Projects\WordTests\ConsoleApplication1\Documents\Doc2.doc", doc, true);
doc.SaveAs(ref fileName,
ref missing, ref missing, ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing, ref missing, ref missing);
}
finally
{
doc.Close(ref missing, ref missing, ref missing);
}
}
finally
{
WordApplication.Quit(ref missing, ref missing, ref missing);
}
}
private void AddDocument(string path, Document doc, bool lastDocument)
{
object subDocPath = path;
var subDoc = WordApplication.Documents.Open(ref subDocPath, ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing, ref missing, ref missing,
ref missing, ref missing);
try
{
object docStart = doc.Content.End - 1;
object docEnd = doc.Content.End;
object start = subDoc.Content.Start;
object end = subDoc.Content.End;
Range rng = doc.Range(ref docStart, ref docEnd);
rng.FormattedText = subDoc.Range(ref start, ref end);
if (!lastDocument)
{
InsertPageBreak(doc);
}
}
finally
{
subDoc.Close(ref missing, ref missing, ref missing);
}
}
private static void InsertPageBreak(Document doc)
{
object docStart = doc.Content.End - 1;
object docEnd = doc.Content.End;
Range rng = doc.Range(ref docStart, ref docEnd);
object pageBreak = WdBreakType.wdPageBreak;
rng.InsertBreak(ref pageBreak);
}
private ApplicationClass WordApplication { get; set; }
private object missing = Type.Missing;
}
}