web-dev-qa-db-fra.com

Fusionner plusieurs documents Word en un seul Open Xml

J'ai environ 10 documents Word que je génère en utilisant open xml et d'autres trucs. Maintenant, je voudrais créer un autre document Word et un par un, je voudrais les joindre à ce document nouvellement créé. Je souhaite utiliser le xml ouvert, tout indice serait appréciable. Voici mon code:

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

Mettre à jour (en utilisant 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();
        } 

Pourquoi ce code écrase le contenu du dernier fichier lorsque j'utilise plusieurs fichiers? Mise à jour 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());
            } 
        }

Ce code ajoute les données Test2 deux fois, à la place des données Test1 également. Signifie que je reçois:

Test
Test2
Test2

au lieu de :

Test
Test1
Test2
21
JulyOrdinary

En utilisant le SDK openXML uniquement, vous pouvez utiliser l'élément AltChunk pour fusionner plusieurs documents en un seul.

Ce lien la manière simple d'assembler plusieurs documents Word et celui-ci Comment utiliser altChunk pour l'assemblage de documents fournissent quelques exemples.

EDIT 1

Basé sur votre code qui utilise altchunk dans la question mise à jour (mise à jour # 1), voici le code VB.Net que j'ai testé et qui fonctionne comme un charme pour moi:

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

EDIT 2

Le deuxième problème (mise à jour # 2)

Ce code ajoute les données Test2 deux fois, à la place des données Test1 également.

est lié à altchunkid.

Pour chaque document que vous souhaitez fusionner dans le document principal, vous devez:

  1. ajouter un AlternativeFormatImportPart dans le mainDocumentPart avec un Id qui doit être unique. Cet élément contient les données insérées
  2. ajoutez dans le corps un élément Altchunk dans lequel vous définissez id pour référencer le précédent AlternativeFormatImportPart.

Dans votre code, vous utilisez le même identifiant pour tous les AltChunks. C'est pourquoi vous voyez souvent le même texte.

Je ne suis pas sûr que l'altchunkid sera unique avec votre code: string altChunkId = "AltChunkId" + DateTime.Now.Ticks.ToString().Substring(0, 2);

Si vous n'avez pas besoin de définir une valeur spécifique, je vous recommande de ne pas définir explicitement le AltChunkId lorsque vous ajoutez le AlternativeFormatImportPart. Au lieu de cela, vous en obtenez un généré par le SDK comme ceci:

VB.Net

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

C #

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

Il existe une API Nice wrapper (Document Builder 2.2) autour de xml ouvert spécialement conçue pour fusionner des documents, avec la flexibilité de choisir les paragraphes à fusionner, etc. Vous pouvez le télécharger à partir de ici (mise à jour: déplacé vers - github ).

La documentation et les captures d'écran sur la façon de l'utiliser sont ici .

Mise à jour: exemple de code

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

Les types Source et WmlDocument proviennent de l'API Document Builder.

Vous pouvez même ajouter directement les chemins d'accès aux fichiers si vous choisissez comme:

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

Trouvé Nice Comparison entre AltChunk et Document Builder approches pour fusionner des documents - utile de choisir en fonction de ses exigences.

Vous pouvez également utiliser la bibliothèque DocX pour fusionner des documents, mais je préfère Document Builder à cela pour la fusion de documents.

J'espère que cela t'aides.

9
Flowerking

Facile à utiliser 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();
        }
    }
}
3
yonexbat

La seule chose qui manque dans ces réponses est la boucle for.

Pour ceux qui veulent juste le copier/coller:

void MergeInNewFile(string resultFile, IList<string> filenames)
{
    using (WordprocessingDocument document = WordprocessingDocument.Create(resultFile, WordprocessingDocumentType.Document))
    {
        MainDocumentPart mainPart = document.AddMainDocumentPart();
        mainPart.Document = new Document(new Body());

        foreach (string filename in filenames)
        {
            AlternativeFormatImportPart chunk = mainPart.AddAlternativeFormatImportPart(AlternativeFormatImportPartType.WordprocessingML);
            string altChunkId = mainPart.GetIdOfPart(chunk);

            using (FileStream fileStream = File.Open(filename, FileMode.Open))
            {
                chunk.FeedData(fileStream);
            }

            AltChunk altChunk = new AltChunk { Id = altChunkId };
            mainPart.Document.Body.AppendChild(altChunk);
        }

        mainPart.Document.Save();
    }
}

Tous les crédits vont à Chris et yonexbat

1
Jinjinov