C# 操作通过word模板合并N个word文档
C# 操作通过word模板合并N个word文档。在工作中遇到了这样一个工作场景,客户需要经常对上级上报或者汇报本阶段的工作, 比如:日报、周报、月总结、季度分析、年度汇总等,这些文档的模板下级不能自己创造, 只能按照上级的制定好的文档进行填报,部门领导需要汇总本部分若干个人的汇报成果再合并到一起, 人工量非常之大, 所以这个就是本文产生的场景。
本文介绍通过Aspose.Words这个第三方控件库来操作word,有点事脱离了引用原生word类库。
先把业务场景的图片贴上来, 假设有两个同类型的文档:
1.将使用同一个模板类型的文档按书签把对应的内容合并到对应的章节。
/// <summary>
/// 文件合并,必须是同一类型的文件
/// </summary>
/// <param name="reportClassfication">获取上报报表模板dic_report_classfication</param>
/// <param name="_product">产品,简称产品,本单位编辑的情报;一般是WORD文档,包括普通产品和加密产品。</param>
/// <param name="_needMergeInformations">简称素材,下级单位向本单位上报的情报</param>
/// <returns></returns>
public bool MergeReportDocument(Models.Inteligence.ReportClassification reportClassfication,
Models.Inteligence.ReportIntelProduct _product,
List<Models.Inteligence.ReportIntelInformation> _needMergeInformations)
{
string templateFile = reportClassfication.LocalPath;
if (!File.Exists(templateFile))
{
throw new ArgumentException(templateFile, string.Format("{0} 文件不存在", Path.GetFileName(templateFile)));
}
List<Aspose.Words.Document> _ReportInformationDocs = new List<Aspose.Words.Document>();
try
{
foreach (Models.Inteligence.ReportIntelInformation item in _needMergeInformations)
{
Aspose.Words.Document _inforDoc = null;
string InforFileName = item.File.LocalPath;
_inforDoc = new Aspose.Words.Document(InforFileName);
if (_inforDoc == null)
{
Log.LogHelper.e(item.ID + "open _inforDoc Failed ");
return false;
}
_ReportInformationDocs.Add(_inforDoc);
}
if (!File.Exists(reportClassfication.LocalPath))
{
File.Copy(reportClassfication.LocalPath, _product.File.LocalPath);
}
//创建_product
_ReportProductDoc = new Aspose.Words.Document(templateFile);
}
catch (Exception e)
{
Log.LogHelper.i("Open Word Failed, ", e);
return false;
}
Dictionary<string, List<Aspose.Words.Paragraph>> dictBookMark = new Dictionary<string, List<Aspose.Words.Paragraph>>(); //创建键值对 第一个string 为书签名称 第二个string为要填充的数据
//实例化新建一个空白的文档
Aspose.Words.DocumentBuilder builder = new Aspose.Words.DocumentBuilder(_ReportProductDoc);
try
{ //获取模板的标签
List<string> listAllBookmark = new List<string>();
foreach (Aspose.Words.Bookmark bookmark in _ReportProductDoc.Range.Bookmarks)
{
if (!bookmark.Name.Equals("_GoBack"))
{
listAllBookmark.Add(bookmark.Name);
dictBookMark.Add(bookmark.Name, new List<Aspose.Words.Paragraph>());
}
}
List<Aspose.Words.Paragraph> listParagraph = new List<Aspose.Words.Paragraph>();
for (int j = 0; j < _ReportInformationDocs.Count; j++)
{
//包含书签的段落的索引
int[] bookMarkIndex = new int[listAllBookmark.Count+1];
Aspose.Words.ParagraphCollection paragraphs = GetAllWordParagraphs(_ReportInformationDocs[j]);// WordParagraphs_ReportInformationDocs[j].GetChildNodes(Aspose.Words.NodeType.Paragraph, true);
if (paragraphs != null)
{
GetBookMarkIndexInWordParagraphs(paragraphs, ref bookMarkIndex);//获取索引
for (int loction = 0; loction < listAllBookmark.Count; loction++)
{
listParagraph = dictBookMark[listAllBookmark[loction]];
int rangeNext = GetRangeIndex(loction, bookMarkIndex);//获取两个书签之间的段落索引位置
for (int section = bookMarkIndex[loction]; section < rangeNext; section++)
{
if (rangeNext== paragraphs.Count)
{
listParagraph.Add(paragraphs[section]);
}
else
{
if (section != rangeNext - 1)
{
listParagraph.Add(paragraphs[section]);
}
}
}
}
}
}
foreach (var key in dictBookMark.Keys) //循环键值对
{
builder.MoveToBookmark(key); //将光标移入书签的位置
string strBookmark = "";
foreach (Aspose.Words.Paragraph text in dictBookMark[key])
{
strBookmark += text.GetText();
}
builder.Write(strBookmark);
}
//文件输出路径,包含文件名
//string saveFile = Path.Combine(templateFile, saveFileName);
string fileName = _product.File.LocalPath;
_ReportProductDoc.Save(fileName);
}
catch (Exception e)
{
//if (MessageDxUtil.ShowYesNoAndTips("导出成功,是否打开文件?") == System.Windows.Forms.DialogResult.Yes)
//{
// Process.Start(saveFile);
//}
Log.LogHelper.i("Merge Word Failed, ", e);
return false;
}
return true;
}
2.至此上面完成合并, 但会有一个问题,一旦标签处换行,内容则无法完整合并, 具体看如下图所示文档结构。

为此我们需要遍历DOM结构,将内容获取到然后再合并。
//1.获取所有段落
public Aspose.Words.ParagraphCollection GetAllWordParagraphs(Aspose.Words.Document docfile)
{
if (docfile.FirstSection.Body.Paragraphs.Count > 0)
{
return docfile.FirstSection.Body.Paragraphs;//word中的所有段落
}
return null;
}
//2.获取段落内的书签位置索引
public void GetBookMarkIndexInWordParagraphs(Aspose.Words.ParagraphCollection paragraphs,ref int[] bookMarkIndex)
{
List<int> list = new List<int>();
for (int i = 0; i < paragraphs.Count; i++)
{
if (paragraphs[i].Range.Bookmarks.Count > 0)
{
list.Add(i);
}
}
list.Add(paragraphs.Count);
bookMarkIndex =list.ToArray();
}
//3.返回索引
private int GetRangeIndex(int currentLoc,int[] bookMarkIndex)
{
if (currentLoc<bookMarkIndex.Length)
{
return bookMarkIndex[currentLoc + 1];
}
else
return -1;
}
3.关于Aspose.Words 文档的详细DOM结构,大家可百度搜索。