19 votos

¿Cómo puedo mediante programación usar C# para anexar varios archivos DOCX juntos?

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!

14voto

GRGodoi Puntos 716

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.

7voto

Rob Windsor Puntos 5009

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.

5voto

Mike B Puntos 991

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

2voto

Pete Skelly Puntos 683

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

2voto

Terence Lewis Puntos 504

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

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X