Добавить несколько файлов DOCX вместе - PullRequest
25 голосов
/ 29 октября 2008

Мне нужно использовать C # программно, чтобы добавить несколько существующих docx файлов в один длинный docx файл, включая специальные разметки, такие как маркеры и изображения. Информация верхнего и нижнего колонтитула будет удалена, поэтому ее не будет рядом, чтобы вызвать какие-либо проблемы.

Я могу найти много информации о том, как манипулировать отдельным файлом docx с помощью .NET Framework 3, но нет ничего простого или очевидного в том, как вы будете объединять файлы. Есть также сторонняя программа (Acronis.Words), которая будет это делать, но это непомерно дорого.

Обновление:

Предложена автоматизация через Word, но мой код будет работать в ASP.NET на веб-сервере IIS, поэтому выход в Word для меня не вариант. Извините, что не упомянул об этом.

Ответы [ 7 ]

23 голосов
/ 17 марта 2010

Несмотря на все хорошие предложения и решения, я разработал альтернативу. На мой взгляд, вам следует избегать использования Word в серверных приложениях полностью. Поэтому я работал с OpenXML, но он не работал с AltChunk. Я добавил текст в исходное тело, вместо него я получил список байтов [], а список имен файлов, но вы можете легко изменить код в соответствии со своими потребностями.

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

Надеюсь, это вам поможет.

7 голосов
/ 30 октября 2008

Вам не нужно использовать автоматизацию. Файлы DOCX основаны на форматах OpenXML. Это просто zip-файлы с кучей XML и бинарных частей (думаю, файлов) внутри. Вы можете открыть их с помощью API упаковки (System.IO.Packaging в WindowsBase.dll) и управлять ими с помощью любого из классов XML в Framework.

Проверьте OpenXMLDeveloper.org для подробностей.

5 голосов
/ 23 августа 2012

Это очень поздно для первоначального вопроса и немного изменилось, но я подумал, что поделюсь тем, как я написал свою логику слияния. Это позволяет использовать Open XML Power Tools

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

В настоящее время это работает очень хорошо в нашем приложении. Я немного изменил код, потому что мои требования - чтобы каждый документ сначала обрабатывался. Поэтому передается объект DTO с байтовым массивом шаблона и различными значениями, которые необходимо заменить. Вот как выглядит мой код в данный момент. Что делает код немного дальше.

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;
}
2 голосов
/ 21 января 2009

Вы хотите использовать AltChunks и OpenXml SDK 1.0 (как минимум, 2.0, если можете). Проверьте блог Эрика Уайта для получения более подробной информации и просто как отличный ресурс! Вот пример кода, который должен помочь вам начать работу, если не сработает сразу.

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;
}
2 голосов
/ 29 октября 2008

Некоторое время назад я написал небольшое тестовое приложение для этого. Мое тестовое приложение работало с документами Word 2003 (.doc), а не .docx, но я думаю, что процесс тот же - я должен подумать, что все, что вам нужно изменить, - это использовать более новую версию Первичной сборки взаимодействия. Этот код будет выглядеть намного лучше с новыми функциями 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;
    }
}
0 голосов
/ 22 января 2009

Я подал заявку в C # на объединение файлов RTF в один документ, надеюсь, он будет работать и для файлов DOC и DOCX.

    Word._Application wordApp;
    Word._Document wordDoc;
    object outputFile = outputFileName;
    object missing = System.Type.Missing;
    object vk_false = false;
    object defaultTemplate = defaultWordDocumentTemplate;
    object pageBreak = Word.WdBreakType.wdPageBreak;
    string[] filesToMerge = new string[pageCounter];
    filestoDelete = new string[pageCounter];

    for (int i = 0; i < pageCounter; i++)
    {
        filesToMerge[i] = @"C:\temp\temp" + i.ToString() + ".rtf";
        filestoDelete[i] = @"C:\temp\temp" + i.ToString() + ".rtf";                
    }
    try
    {
        wordDoc = wordApp.Documents.Add(ref missing, ref missing, ref missing, ref missing);
    }
    catch(Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
    Word.Selection selection= wordApp.Selection;

    foreach (string file in filesToMerge)
    {
        selection.InsertFile(file,
            ref missing,
            ref missing,
            ref missing,
            ref missing);

        selection.InsertBreak(ref pageBreak);                                     
    }
    wordDoc.SaveAs(ref outputFile, 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);

Надеюсь, это поможет!

0 голосов
/ 16 января 2009

Это сложный процесс, поэтому код выходит за рамки поста на форуме, я бы написал ваше приложение для вас, но подведу итог.

  • Открыть оба документа как пакеты
  • Перебирайте части второго документа, ища изображения и вставляйте вещи
  • Добавьте эти части в первый пакет, помня новые идентификаторы отношений (это включает в себя много работы потока)
  • откройте часть document.xml во втором документе и замените все старые идентификаторы отношений новыми. Добавьте все дочерние узлы, но не корневой узел второго document.xml, в первый document.xml
  • сохранить все документы Xml и очистить пакет
...