プログラムでC#を使用して、いくつかの既存のdocx
ファイルを単一の長いdocx
ファイルに追加する必要があります。これには、箇条書きや画像などの特別なマークアップが含まれます。ヘッダーとフッターの情報は削除されるため、問題が発生することはありません。
個々のdocx
ファイルを.NETFramework 3で操作する方法についてはたくさんの情報を見つけることができますが、ファイルをマージする方法について簡単で明白なことは何もありません。それを実行するサードパーティのプログラム(Acronis.Words)もありますが、非常に高価です。
Wordを介した自動化が提案されていますが、私のコードはIIS Webサーバー上のASP.NETで実行されるため、Wordにアクセスすることはできません。言及しないで申し訳ありません。そもそもそれ。
提出されたすべての良い提案と解決策にもかかわらず、私は代替案を開発しました。私の意見では、サーバーアプリケーションで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);
}
}
}
これがお役に立てば幸いです。
自動化を使用する必要はありません。 DOCXファイルはOpenXML形式に基づいています。それらは、XMLとバイナリ部分(ファイルを考えてください)が内部に含まれている単なるZipファイルです。それらをPackagingAPI(WindowsBase.dllのSystem.IO.Packaging)で開き、フレームワークの任意のXMLクラスで操作できます。
詳細については、 OpenXMLDeveloper.org を確認してください。
これは元の質問に非常に遅れており、かなりの変更がありますが、マージロジックの記述方法を共有したいと思いました。これは 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;
}
AltChunksとOpenXmlSDK 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;
}
これを行うために、少し前に小さなテストアプリを作成しました。私のテストアプリは.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;
}
}
非常に複雑なので、コードはフォーラムの投稿の範囲外です。私はあなたのためにあなたのアプリを書いていますが、要約すると。
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);
お役に立てれば!