28

C# をプログラムで使用して、箇条書きや画像などの特別なマークアップを含むいくつかの既存docxのファイルを 1 つの長いファイルに追加する必要があります。docxヘッダーとフッターの情報は削除されるため、問題が発生することはありません。

.NET Framework 3 を使用して個々のファイルを操作する方法については多くの情報を見つけることができますが、docxファイルをマージする方法については簡単でも明白でもありません。それを行うサードパーティ プログラム (Acronis.Words) もありますが、非常に高価です。

アップデート:

Word による自動化が提案されましたが、私のコードは IIS Web サーバー上の ASP.NET で実行される予定なので、Word に移行することはできません。最初にそれについて言及しなかったことをお詫びします。

4

8 に答える 8

27

提出されたすべての良い提案と解決策にもかかわらず、私は代替案を開発しました。私の意見では、サーバーアプリケーションでWordを使用することは完全に避けるべきです。そのため、OpenXMLを使用しましたが、AltChunkでは機能しませんでした。元の本文にテキストを追加しました。ファイル名のリストの代わりにbyte[]のリストを受け取りますが、必要に応じてコードを簡単に変更できます。

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

これがお役に立てば幸いです。

于 2010-03-17T15:51:38.917 に答える
7

自動化を使用する必要はありません。DOCXファイルはOpenXML形式に基づいています。それらは、XMLとバイナリ部分(ファイルと考えてください)が内部に含まれている単なるzipファイルです。それらをPackagingAPI(WindowsBase.dllのSystem.IO.Packaging)で開き、Frameworkの任意のXMLクラスで操作できます。

詳細については、 OpenXMLDeveloper.orgをご覧ください。

于 2008-10-30T02:23:50.640 に答える
5

これは元の質問に非常に遅れており、かなりの変更がありますが、マージロジックを記述した方法を共有したいと思いました。これは、Open XMLPowerToolsを利用します

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;
}
于 2012-08-23T10:29:40.000 に答える
2

これを行うために、少し前に小さなテストアプリを作成しました。私のテストアプリは.docxではなくWord2003ドキュメント(.doc)で動作しましたが、プロセスは同じだと思います。変更する必要があるのは、新しいバージョンのプライマリ相互運用機能アセンブリを使用することだけだと思います。このコードは、新しい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;
    }
}
于 2008-10-29T20:13:47.457 に答える
2

AltChunks と OpenXml SDK 1.0 (できれば 2.0 以上) を使用する必要があります。詳細と優れたリソースについては、Eric White のブログをご覧ください。これは、すぐに機能しない場合でも、開始するためのコード サンプルです。

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;
}
于 2009-01-21T13:22:13.447 に答える
0

複雑なので、コードはフォーラムの投稿の範囲外です。私はあなたのためにあなたのアプリを書いていますが、要約すると.

  • 両方のドキュメントをパッケージとして開く
  • 2 番目のドキュメントのパーツをループして、画像や埋め込まれたものを探します
  • これらのパーツを最初のパッケージに追加して、新しい関係 ID を記憶します (これには多くのストリーム作業が含まれます)。
  • 2 番目のドキュメントの document.xml 部分を開き、すべての古い関係 ID を新しいものに置き換えます。2 番目の document.xml のルート ノードではなく、すべての子ノードを最初の document.xml に追加します。
  • すべての XmlDocuments を保存し、パッケージをフラッシュします
于 2009-01-15T21:48:26.870 に答える
0

RTFファイルを1つのドキュメントにマージするアプリケーションをC#で作成しましたが、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);

お役に立てれば!

于 2009-01-22T20:13:31.773 に答える