Подтвердить что ты не робот

Объединение нескольких текстовых документов в один Open Xml

У меня есть около 10 текстовых документов, которые я генерирую с помощью открытого xml и другого материала. Теперь я хотел бы создать еще один документ и один за другим, я хотел бы присоединиться к ним в этот вновь созданный документ. Я хочу использовать открытый xml, любой намек был бы заметным. Ниже мой код:

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

Обновление (с помощью 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();
        } 

Почему этот код перезаписывает содержимое последнего файла, когда я использую несколько файлов? Обновление 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());
            } 
        }

Этот код добавляет данные Test2 дважды, вместо данных Test1. Значит, я получаю:

Test
Test2
Test2

вместо:

Test
Test1
Test2
4b9b3361

Ответ 1

Используя только OpenXML SDK, вы можете использовать AltChunk, чтобы объединить несколько документов в один.

Эта ссылка the-easy-way-to-assemble-multiple-word-documents и эта Как использовать altChunk для Сборка документов содержат некоторые примеры.

РЕДАКТИРОВАТЬ 1

На основе вашего кода, который использует AltChunk в обновленном вопросе (обновление # 1), вот код VB.Net, который я тестировал, и который работает как прелесть для меня:

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

РЕДАКТИРОВАТЬ 2

Вторая проблема (обновление # 2)

Этот код дважды добавляет данные Test2, вместо данных Test1, как хорошо.

связан с altchunkid.

Для каждого документа, который вы хотите объединить в основном документе, вам необходимо:

  • добавить AlternativeFormatImportPart в mainDocumentPart с Id , который должен быть уникальным. Этот элемент содержит вложенные данные
  • добавьте в тело элемент AltChunk, в котором вы установите Id для ссылки на предыдущий AlternativeFormatImportPart.

В вашем коде вы используете один и тот же идентификатор для всех AltChunks. Это почему вы видите много раз тот же текст.

Я не уверен, что altchunkid будет уникальным с вашим кодом: string altChunkId = "AltChunkId" + DateTime.Now.Ticks.ToString().Substring(0, 2);

Если вам не нужно устанавливать определенное значение, я рекомендую не устанавливать явно altchunkid при добавлении AlternativeFormatImportPart. Вместо этого вы получаете один, сгенерированный SDK, следующим образом:

VB.Net

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

С#

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

Ответ 2

Существует хороший API-интерфейс обложки (Document Builder 2.2) вокруг открытого xml, специально разработанного для объединения документов, с гибкостью выбора абзацев для слияния и т.д. Вы можете загрузить его из здесь.

Документация и скриншоты о том, как ее использовать, здесь.

Обновление: пример кода

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

Типы Source и WmlDocument относятся к API-интерфейсу Document Builder.

Вы даже можете добавить пути к файлам напрямую, если вы выбрали:

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

Нашли это Nice Comparison между подходами AltChunk и Document Builder для объединения документов - полезно выбирать на основе требований.

Вы также можете использовать библиотеку DocX для объединения документов, но я предпочитаю Document Builder над этим для слияния документов.

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

Ответ 3

Прост в использовании на С#:

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