最近,我写公司项目word导出功能,应该只有2小时的工作量,却被硬生生的拉长2天,项目上线到业务正常运行也被拉长到2个星期。

为什么如此浪费时间呢?

1)公司的项目比较老,采用硬编码模式,意味着word改一个字就要发布一次代码。发布检验就浪时间了。

2)由于硬编码,采用的是<html>这种格式,手写代码比较废时,而且编写表格时会遇到单元格字数变多被撑大,表格变形的情况。表格长度需要人工计算。这类意想不到的问题。

3)公司测试库数据不全,测试库数据无法全面覆盖线上环境。这又拉长了检验时间。

4)项目分支被正在开发的分支合并了,一下子被拉长了4天。

这简单功能浪费太多时间了,我在网上搜了一下word导出的方案:

第一种:硬编码,就是公司的方案,问题太多了不用考虑。

第二种:通过Sql查询数据,存入字典,再通过第三方组件替换word的文字。这种方案,简单容操作,sql查询可以换成存储过程,也存在缺点,1)存储过程要写提很细,逻辑算法都写在存储过程,存储过程可能变得很复杂。2)不支持表格内插入多条数据。

第三种:通过Sql查询数据,使用Razor模板引擎生成word。这种方案解决了存储过程复杂问题,但Razor模板内使用<html>这种格式,所以写模板时很麻烦。

第四种:通过Sql查询数据,存入字典,再通过第三方组件替换word的域。这种方案与第二种方案类似,对我个人来说,我不喜欢修改域。

但是,我想要一个简单、容易控制、表格内能插入多条数据、可商用的方案。

简单:类似第二种方案,数据存入字典,循环替换word的文字,存储过程可以写得简单。

容易控制:模板不能使用<html>这种格式,最好能用office直接控制表格文字大小、颜色。

表格内能插入多条数据:我写的组件内必须有索引。

可商用:拒绝商用组件。

经过几天琢磨,我找到可行的方案:存储过程+模板+算法可控

依赖组件:

DocumentFormat.OpenXml,微软官方开源组件,支持docx文件,MIT协议。

ToolGood.Algorithm,本人的Excel计算引擎组件,MIT协议,可简化存储过程。

核心代码:

ReplaceTemplate 替换Word文字

ReplaceTable 替换Word表格并支持插入

ReplaceTemplate 替换Word文字

public class WordTemplate : AlgorithmEngine{private readonly static Regex _tempEngine = new Regex("^###([^::]*)[::](.*)$");// 定义临时变量private readonly static Regex _tempMatch = new Regex("(#[^#]+#)");// private readonly static Regex _simplifyMatch = new Regex(@"(\{[^\{\}]*\})");//简化文本 只读取字段private void ReplaceTemplate(Body body){var tempMatches = new List<string>();List<Paragraph> deleteParagraph = new List<Paragraph>();foreach (var paragraph in body.Descendants<Paragraph>()) {var text = paragraph.InnerText.Trim();var m = _tempEngine.Match(text);if (m.Success) {var name = m.Groups[1].Value.Trim();var engine = m.Groups[2].Value.Trim();var value = this.TryEvaluate(engine, "");this.AddParameter(name, value);deleteParagraph.Add(paragraph);continue;}var m2 = _tempMatch.Match(text);if (m2.Success) {tempMatches.Add(m2.Groups[1].Value);continue;}var m3 = _simplifyMatch.Match(text);if (m3.Success) {tempMatches.Add(m3.Groups[1].Value);continue;}}foreach (var paragraph in deleteParagraph) {paragraph.Remove();}Regex nameReg = new Regex(string.Join("|", listNames));foreach (var m in tempMatches) {string value;if (m.StartsWith("#")) {var eval = m.Trim('#');……value = this.TryEvaluate(eval, "");} else {value = this.TryEvaluate(m.Replace("{", "[").Replace("}", "]"), "");}foreach (var paragraph in body.Descendants<Paragraph>()) {ReplaceText(paragraph, m, value);}}}
// 代码来源 https://stackoverflow.com/questions/19094388/openxml-replace-text-in-all-documentprivate void ReplaceText(Paragraph paragraph, string find, string replaceWith){….
}
}

ReplaceTable 替换Word表格并支持插入

private readonly static Regex _rowMatch = new Regex(@"({{(.*?)}})");//private int _idx;private List<string> listNames = new List<string>();private void ReplaceTable(Body body){foreach (Table table in body.Descendants<Table>()) {foreach (TableRow row in table.Descendants<TableRow>()) {bool isRowData = false;foreach (var paragraph in row.Descendants<Paragraph>()) {var text = paragraph.InnerText.Trim();if (_rowMatch.IsMatch(text)) {isRowData = true;break;}}if (isRowData) {// 防止 list[i].Id 写成  [list][[i]].Id 这种繁杂的方式Regex nameReg = new Regex(string.Join("|", listNames));Dictionary<string, string> tempMatches = new Dictionary<string, string>();foreach (Paragraph ph in row.Descendants<Paragraph>()) {var m2 = _rowMatch.Match(ph.InnerText.Trim());if (m2.Success) {var txt = m2.Groups[1].Value;var eval = txt.Substring(2, txt.Length - 4).Trim();eval = nameReg.Replace(eval, new MatchEvaluator((k) => {return "[" + k.Value + "]";}));tempMatches[txt] = eval;}}TableRow tpl = row.CloneNode(true) as TableRow;TableRow lastRow = row;TableRow opRow = row;var startIndex = UseExcelIndex ? 1 : 0;_idx = startIndex;while (true) {if (_idx > startIndex) { opRow = tpl.CloneNode(true) as TableRow; }bool isMatch = true;foreach (var m in tempMatches) {string value = this.TryEvaluate(m.Value, null);if (value == null) {isMatch = false;break;}foreach (var ph in opRow.Descendants<Paragraph>()) {ReplaceText(ph, m.Key, value);}}if (isMatch==false) {//当数据为空时,清空数据if (_idx == startIndex) {foreach (var ph in opRow.Descendants<Paragraph>()) {ph.RemoveAllChildren();}}break;}if (_idx > startIndex) { table.InsertAfter(opRow, lastRow); }lastRow = opRow;_idx++;}}}}}

案例上手:

后台代码:

// 获取数据var helper = SqlHelperFactory.OpenSqliteFile("test.db");.......var dt = helper.ExecuteDataTable("select * from Introduction");var tableTests = helper.Select<TableTest>("select * from TableTest");ToolGood.OutputWord.WordTemplate openXmlTemplate = new ToolGood.OutputWord.WordTemplate();// 加载数据openXmlTemplate.SetData(dt);openXmlTemplate.SetListData("list", JsonConvert.SerializeObject(tableTests));// 生成模板 一openXmlTemplate.BuildTemplate("test.docx", "openxml_2.docx");// 生成模板 二var bs = openXmlTemplate.BuildTemplate("test.docx");File.WriteAllBytes("openxml_1.docx", bs);

Word模板:

Word生成后:

后记:

WordTemplate 类,主要实现了三个功能:

1、自定义替换word中的文字标签,当标签不存在,则设置为空字符串;

2、可以有word中定义公式,替换所对应的值;

3、在表格插入多行数据,当数据为0时清空单元格。

通过上面三个功能,WordTemplate 类将代码中的word生成方法分离出来。

系统后台需要配置 存储过程与word模板信息,就可以将word生成与系统更新完成分离开了。

系统后台可以配置公式,则公式修改不需要更新word模板。

注:一般业务人员是看得懂四则运算的,部分财务人员更是了解Excel公式,可以减少开发协助时间。

完整代码:https://github.com/toolgood/ToolGood.OutputWord

该组件已上传到Nuget:Install-Package ToolGood.OutputWord

Excel公式参考:https://github.com/toolgood/ToolGood.Algorithm

简单快速导出word文档相关推荐

  1. 5导出word_妙招技法 | 教你如何快速导出Word文档中的图片

    关注[新精英充电站]能力提升看得见! 我们在翻阅一些比较好的Word文档时,经常想要将文档中用到的一些好的图片素材保留下来,但却苦于不知道怎么保存,最后只有无奈放弃. 其实,Word中提供有导出图片的 ...

  2. 如何导出word文档

    前言 一般导出word,是后台做的功能,如果确实需要前端做,可以借助jQuery的wordexport.js导出word文档 提示:以下是本篇文章正文内容,下面案例可供参考 一.使用步骤 1.引入文件 ...

  3. PHP导出word文档的简单实现方法(可导出图片)

    这是看了网上几篇关于PHP导出word文档的文章之后,本人改进一下的方法,可以导出带图片的,以下是demo. $row = M('Article')->where(array('id'=> ...

  4. 【Java用法】使用Java导出word文档的解决方案(适用于从服务器上下载到本地电脑)

    本文目录 一.Controller 二.Service 接口类 三.ServiceImpl 实现类 四.Content-Type 类型与MIME Type类型对照表 最近在做一个word导出功能,需求 ...

  5. 【Java用法】使用Java导出word文档的解决方案(适用于Windows电脑)

    目录 实现方式一.通过原生的POI 实现方式二.通过Hutool工具包 步骤1.添加pom依赖 步骤2.编写几行代码 步骤3.启动项目,大功告成 实现的效果 最近在做一个word导出功能,需求非常简单 ...

  6. java导出word表格_使用PowerDesigner16.5 逆向数据库 导出word文档

    在上一篇<使用PowerDesigner16.5 逆向数据库生产表结构或导出word文档二>中,我们学会了使用PowerDesigner16.5怎么连接数据库,逆向生成表结构.有时候,我们 ...

  7. 【Python】批量导出word文档中的图片、嵌入式文件

    Python 批量导出word文档中的图片.嵌入式文件 需求 学生试卷中的题目有要提交截图的,也有要提交文件的,为了方便学生考试,允许单独交或者嵌入Word中提交,那么事后如何整理学生的答案?单独提交 ...

  8. 【Java实现导出Word文档功能 XDocReport +FreeMarker】

    Java实现导出Word文档功能(XDocReport +FreeMarker) 前言 在日常的开发工作中,我们时常会遇到导出Word文档报表的需求,比如公司的财务报表.医院的患者统计报表.电商平台的 ...

  9. jQuery表格导出Excel文件以及网页内容导出Word文档

    前言: 我在这里给大家介绍一下我们能用代码实现的最简单的两种转换为Excel文件以及Word文档的方法. jQuery表格导出Excel文件 先来看看我们的层级划分: 如图所示: demo.css 代 ...

最新文章

  1. 把Excel文件中的数据读入到DataGrid中
  2. java设置缓存机制
  3. C++ decltype类型指示符
  4. Chapter10:观察者模式
  5. 【计算机网络】网络层 : ICMP 协议 ( ICMP 差错报文 | 差错报文分类 | ICMP 询问报文 | ICMP 应用 | Ping | Traceroute )
  6. Source Insight,修改字体
  7. c++性能之对象与指针性能比较、以及java与c++性能对比实测
  8. python写appium_案例分享,Python +appium做登录功能
  9. 清零 css,css样式清零及常用类
  10. shell编程:对话 UNIX: 更多 shell 脚本技术
  11. 微软将开源查询处理工具Trill,怎么下载部署?
  12. Perl语言入门14-17
  13. PDMS二次开发(十)——螺栓材料统计功能重构开发介绍
  14. android两个popwindow背景,Android PopWindow 设置背景亮度的实例
  15. Linux proc目录详解
  16. 介绍分享几款免费的在线Web文件管理器
  17. [pyecharts学习笔记]——Pie饼图
  18. 2012年桂城街道小学毕业生升初中
  19. java基于ssm的万卷图书馆借阅管理平台
  20. matlab计算滤波器品质因数,滤波器品质因数概述

热门文章

  1. js 控制超出字数显示省略号
  2. adb不识别设备(手机)的若干情形及解决方法
  3. Ant—使用Ant构建一个简单的Java工程(两)
  4. Android -- 创建XML文件对象及其序列化, pull解析XML文件
  5. 如何使用Amazon Echo控制您的Eero Wi-Fi网络
  6. Cygwin使用指南
  7. 系统学习redis之二——redis集群搭建
  8. “爱思助手”曝为iOS木马:可绕过苹果DRM机制
  9. HDU1232 畅通工程
  10. phpmyadmin的安装部署