Web应用Word生成
前段时间接到一个Web应用自动生成Word的需求,现整理了下一些关键步骤拿来分享一下。
思路:(注:这里只针对WORD2003版本,其它版本大同小异。)
因为WORD文件内部的数据及格式等是通过XML文件的形式存储的,所以WORD文件可以很方便的实现由DOC到XML格式的相互转换,而操作XML文件就方便的多了,这样就实现了与平台无关的各种操作,通过节点的查询、替换、删除、新增等生成Word文件。所以,根据模板生成WORD文件实质就是由用户数据替换XML文件中特殊标签,然后另存为一个DOC文件的过程。
下面列举涉及到的一些关键步骤(以介绍信为例)
第一步:根据需求制作WORD模板
新建一个DOC格式的WORD文件,根据需要填写好模板内容,设置好模板的格式,包括字体,样式,空行等等,需要填充的数据使用特殊标签(如:【※单位名称※】)预先占位,然后将新建的WORD文件另存为XML格式文件。这样, WORD模板就制作完成了,代码如下:
第二步:在配置文件中配置好模板信息
新增名为template-rule.xml的配置文件,每个template节点对应一个模板类型。每个template中有一个taglist节点,该节点包含的所有子节点包含了模板所有将要替换、删除节点信息,节点信息包括:节点值,节点属性英文名称,中文描述,字段类型,可否删除等信息。在设置这个配置文件时候,需要注意desc属性的值必须与模板XML中的占位符一致。比如:模板XML中设置的年份这个录入项【※年※】需与template-rule.xml中的desc="年"名称对应,代码如下:
templates !-- 说明: S-字符串; D-日期; E-金额; M-大写金额; ifEmptyDelete: T-值为空删除父节点,默认为F -- template name="RECOMMEND-LETTER" desc="介绍信" templateFile="template4.xml" taglist remark="单值标签列表" tag id="1" name="ToPartment" desc="接收部门" type="S" ifEmptyDelete="T" #ToPartment /tag !--接收部门-- tag id="2" name="OwnerName" desc="姓名" type="S" #OwnerName /tag !--姓名-- tag id="3" name="CountNum" desc="人数" type="S" #CountNum /tag !--人数-- tag id="4" name="Business" desc="内容" type="S" #Business /tag !--内容-- tag id="5" name="UsefulDays" desc="有效期" type="S" #UsefulDays /tag !--有效期-- tag id="6" name="Year" desc="年" type="S" #Year /tag !--年-- tag id="7" name="Month" desc="月" type="S" #Month /tag !--月-- tag id="8" name="Day" desc="日" type="S" #Day /tag !--日-- /taglist /template /templates
@SuppressWarnings("unchecked") public Template loadRules(Map String, String ruleValue) { InputStream in = null; Template template = new Template(); // 规则配置文件路径 String ruleFile = "template-rule.xml"; // 模板规则名称 String templateRuleName = ""; try { templateRuleName = ruleValue.get("ruleName"); // 读取模板规则文件 in = this.getClass().getClassLoader().getResourceAsStream(ruleFile); // 解析模板规则 SAXBuilder sb = new SAXBuilder(); Document doc = sb.build(in); Element root = doc.getRootElement(); // 得到根元素 List Element templateList = root.getChildren();// 所有模板配置 Element element = null; Vector RuleDTO rules = null; for (int i = 0; i templateList.size(); i++) {// 遍历所有模板 element = (Element) templateList.get(i); String templateName = element.getAttributeValue("name"); if (templateRuleName.equalsIgnoreCase(templateName)) {// 查找给定的模板配置 template.setName(templateName); template.setDesc(element.getAttributeValue("desc")); template.setTemplateFile(element .getAttributeValue("templateFile")); List Element tagList = ((Element) element.getChildren() .get(0)).getChildren();// tag列表 Element tag = null; RuleDTO ruleDTO = null; rules = new Vector RuleDTO for (int j = 0; j tagList.size(); j++) { tag = (Element) tagList.get(j); ruleDTO = new RuleDTO(); ruleDTO.setParmName(tag.getAttributeValue("name")); ruleDTO.setParmDesc("【※" + tag.getAttributeValue("desc") + "※】"); ruleDTO.setParmSeq(tag.getAttributeValue("id")); ruleDTO.setParmType(tag.getAttributeValue("type")); if ("T".equalsIgnoreCase(tag .getAttributeValue("ifEmptyDelete"))) {// 是否可删除标记 ruleDTO.setIfEmptyDelete("T"); } else { ruleDTO.setIfEmptyDelete("F"); } ruleDTO.setParmRegular(tag.getText()); // 值 // 判断参数类型 String value = (String) ((Map String, String ) ruleValue) .get(ruleDTO.getParmRegular().replaceAll("#", "")); ruleDTO.setParmValue(value); rules.add(ruleDTO); } template.setRules(rules); break; } } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (JDOMException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { in.close(); } catch (Exception e) { e.printStackTrace(); } } return template; } /** * 查找父节点 */ public Element findElement(Element currNode, String parentNodeId) { // 节点标示为空 if (currNode == null || parentNodeId == null) { return null; } Element pNode = null; do { pNode = currNode.getParent(); currNode = pNode; } while (parentNodeId.equalsIgnoreCase(pNode.getName())); return pNode; } /** * 生成Word文件 */ @SuppressWarnings("unchecked") public String build(Template template) { InputStream in = null; OutputStream fo = null; // 生成文件的路径 String file = "d:\\test\\" + template.getDesc() + ".doc"; try { // 读取模板文件 in = this.getClass().getClassLoader() .getResourceAsStream(template.getTemplateFile()); SAXBuilder sb = new SAXBuilder(); Document doc = sb.build(in); Element root = doc.getRootElement(); // 得到根元素 Namespace ns = root.getNamespace();// NameSpace // word 03模板存在 wx:sect 元素 List Element sectList = root.getChild("body", ns).getChildren(); Element sectElement = (Element) sectList.get(0); // w:p 下的标签集合 List Element pTagList = sectElement.getChildren("p", ns); // w:tbl 下的标签集合 List Element tblTagList = sectElement.getChildren("tbl", ns); if (pTagList != null pTagList.size() 0) { changeValue4PTag(pTagList, template.getRules(), ns, null); } if (tblTagList != null tblTagList.size() 0) { changeValue4TblTag(tblTagList, template.getRules(), ns); } // 写文件 XMLOutputter outp = new XMLOutputter(" ", true, "UTF-8"); fo = new FileOutputStream(file); outp.output(doc, fo); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (JDOMException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { in.close(); fo.close(); } catch (Exception e) { e.printStackTrace(); } } return file; } /** * 针对 w:body wx:sect w:p 这种层级的WORD模板, 查找及替换 w:p 下的标签。 * @param pTagList : w:p 集合 * @param rulesValue :RuleDTO集合 * @param ns :NameSpace对象 * @param trChildren : w:tbl 的子节点 w:tr 集合 */ @SuppressWarnings("unchecked") private boolean changeValue4PTag(List Element pTagList, Vector RuleDTO rulesValue, Namespace ns, List Element trChildren) { Element p = null; boolean delFlag = false; for (int i = 0; i pTagList.size(); i++) { boolean delCurrNode = false;// 删除当前节点 boolean delCurrNode4TabWR = false;// 删除table中单行节点 p = (Element) pTagList.get(i); List Element pChild = p.getChildren("r", ns); for (int j = 0; pChild != null j pChild.size(); j++) { Element pChildren = (Element) pChild.get(j); Element t = pChildren.getChild("t", ns); if (t != null) { String text = t.getTextTrim(); if (text.indexOf("【※") != -1) { for (int v = 0; v rulesValue.size(); v++) { RuleDTO dto = (RuleDTO) rulesValue.get(v); if (text.indexOf(dto.getParmDesc().trim()) != -1) { // 判断属性值是否为可空删除 if ("T".equals(dto.getIfEmptyDelete()) StringUtils.isBlank(dto .getParmValue())) { // 删除该节点顶级节点 text = ""; if (trChildren != null) {// 针对 w:tbl 删除该行 Element element = ((Element) p .getParent()).getParent(); trChildren.remove(element); delCurrNode4TabWR = true; } else {// 针对 w:r 删除段 // pTagList.remove(p); pTagList.remove(pChildren); delCurrNode = true; } break; } else { text = text.replaceAll(dto.getParmDesc() .trim(), dto.getParmValue()); } } } t.setText(text); } if (delCurrNode4TabWR) {// w:tbl TABLE下的行节点已删除 delFlag = true; break; } else if (delCurrNode) {// w:p 下的节点已删除 i--; delFlag = true; break; } } } } return delFlag; } /** * 针对含有表格的WORD模板, 查找及替换 w:tbl 下的标签。 * @param tblTagList : w:tbl 集合 * @param rulesValue :RuleDTO集合 * @param ns :NameSpace对象 */ @SuppressWarnings("unchecked") private void changeValue4TblTag(List Element tblTagList, Vector RuleDTO rulesValue, Namespace ns) { Element p = null; for (int i = 0; tblTagList != null i tblTagList.size(); i++) { p = (Element) tblTagList.get(i); List Element trChildren = p.getChildren("tr", ns); for (int j = 0; trChildren != null j trChildren.size(); j++) {// 循环 w:tr Element pChildren = (Element) trChildren.get(j); List Element tcTagList = pChildren.getChildren("tc", ns); for (int c = 0; tcTagList != null c tcTagList.size(); c++) {// 循环 w:tc 取 w:p 集合 Element tcChildren = (Element) tcTagList.get(c); List Element pTagList = tcChildren.getChildren("p", ns); boolean delFlag = changeValue4PTag(pTagList, rulesValue, ns, trChildren); if (delFlag) {// 删除行后需要改变trChildren的指针位置 j--; } } } } } public static void main(String[] args) throws Exception { WordBuilder word = new WordBuilder(); Map String, String map = new HashMap String, String //填充参数 map.put("ToPartment", "XXX公司"); map.put("OwnerName", "张三"); map.put("CountNum", "5"); map.put("Business", "例行检查"); map.put("UsefulDays", "15"); map.put("Year", "2014"); map.put("Month", "5"); map.put("Day", "13"); map.put("ruleName", "RECOMMEND-LETTER"); Template template = word.loadRules(map); //直接打开文件 Runtime.getRuntime().exec("explorer " + word.build(template)); }
几点总结及注意事项:
1. 定义的元素name必须与template_rule.xml中对应相同的name的值一致,否则需要设置转换规则。
2. 模板xml中定义的占位符【※※】中的文字必须与template_rule.xml中对应的desc相同,否则需要设置转换规则.
3. 在配置好模板XML后,需要检查 w:body 标签下的子节点是否是 wx:sect 标签(与WORD版本有关),如果没有,则必须加上该标签。
4. 如果要动态删除 w:p 标签节点,则这个节点的内容需要在模板中的同一行,如果不是,则可以手动调整模板XML。
5. 如果需要实现WORD自动换行功能(关于模板中换行的方案暂没有想到更好的),则需要首先计算出对应模板该行的字数,然后采用空格填充来实现。
[AliFlutter]Flutter for Web在无影中的应用 无影是使用Flutter的重度用户,无论是在成熟的移动Android、iOS上,还是桌面端MacOS、Windows、还有各种硬件终端上(Linux)上都有应用 ![](https://ata2-img.oss-cn-zhangjiakou.aliyuncs.com/neweditor/c2434612-86ee-4fb6-a7d1-1622eb6d050d.png) 今年无影使用Flutt
天人合一物我相融,站点升级渐进式Web应用PWA(Progressive Web Apps)实践 PWA(Progressive web apps,渐进式 Web 应用)使用现代的 Web API 以及传统的渐进式增强策略来创建跨平台 Web 应用程序,说白了,PWA可以让我们的站点以原生APP的形式运行,但相比于安装原生APP应用,访问PWA显然更加容易和迅速,还可以通过链接来分享PWA应用。
相关文章
- jQuery入门(4)jQuery中的Ajax应用
- 72 个网络应用安全实操要点,全方位保护 Web 应用的安全
- IDEA中利用MAVEN制作和打包普通可执行应用(非SprintBoot的WEB应用)
- 阿里云 ACK One 多集群管理全面升级:多集群服务、多集群监控、两地三中心应用容灾
- 从 VLAN 到 IPVLAN: 聊聊虚拟网络设备及其在云原生中的应用
- 大数据应用:引领数据浪潮创新商业价值
- 数据挖掘时功能和一个聚类分析应用案例
- docker 应用篇————swarm[二十]
- 构建一个用于产品介绍的WEB应用
- HTML5 Web Workers来加速您的移动Web应用
- 在C#代码中应用Log4Net(四)在Winform和Web中捕获全局异常
- 【斗医】【9】Web应用开发20天
- Spring在web开发中的应用
- SAP Freestyle UI5 和 SAP Fiori Elements 应用的区别
- 最简单的nginx教程 - 如何把一个web应用部署到nginx上
- Angular 服务器端渲染应用的一个错误消息 type ReferenceError - localStorage is not defined
- SAP CRM Fiori 应用的 Deep Create 行为和实现分析
- CRM Fiori my note应用的后台ABAP实现
- Angular应用里的Template Reference变量
- Web应用防火墙最佳实践:恶意IP封禁
- 〖Python WEB 自动化测试实战篇⑬〗- 实战 - 数据驱动在自动化测试中的应用
- Django3.0入门教程【二】:创建第一个应用
- React.js 集成 Kotlin Spring Boot 开发 Web 应用实例详解
- android开机启动应用和服务
- Maven实现Web应用集成測试自己主动化 -- 部署自己主动化(WebTest Maven Plugin)
- 对一个即将上线的网站,如何做一个较完整的Web应用/网站测试?
- .Net Core 获取应用物理路径的常见问题
- 分块传输编码在WEB安全中的应用——sql注入用,可以直接复用chunkSplitPostData还原
- JAX-RS介绍——Java API forRESTful WebServices,JAX-RS的目标是Web Services开发(这与HTML Web应用不同)而Spring MVC的目标则是Web应用开发