Web应用Word生成

简介:

    前段时间接到一个Web应用自动生成Word的需求,现整理了下一些关键步骤拿来分享一下。

 

思路:(注:这里只针对WORD2003版本,其它版本大同小异。)

因为WORD文件内部的数据及格式等是通过XML文件的形式存储的,所以WORD文件可以很方便的实现由DOC到XML格式的相互转换,而操作XML文件就方便的多了,这样就实现了与平台无关的各种操作,通过节点的查询、替换、删除、新增等生成Word文件。所以,根据模板生成WORD文件实质就是由用户数据替换XML文件中特殊标签,然后另存为一个DOC文件的过程。

 

下面列举涉及到的一些关键步骤(以介绍信为例)

第一步:根据需求制作WORD模板

新建一个DOC格式的WORD文件,根据需要填写好模板内容,设置好模板的格式,包括字体,样式,空行等等,需要填充的数据使用特殊标签(如:【※单位名称※】)预先占位,然后将新建的WORD文件另存为XML格式文件。这样, WORD模板就制作完成了,代码如下:

 template4.xml
 

第二步:在配置文件中配置好模板信息

新增名为template-rule.xml的配置文件,每个template节点对应一个模板类型。每个template中有一个taglist节点,该节点包含的所有子节点包含了模板所有将要替换、删除节点信息,节点信息包括:节点值,节点属性英文名称,中文描述,字段类型,可否删除等信息。在设置这个配置文件时候,需要注意desc属性的值必须与模板XML中的占位符一致。比如:模板XML中设置的年份这个录入项【※年※】需与template-rule.xml中的desc=""名称对应,代码如下:

  

Template-rule.xml代码   收藏代码
  1. <?xml version="1.0" encoding="GB2312"?>  
  2. <!-- 模板定义 -->  
  3. <templates>  
  4.     <!-- 说明: S-字符串; D-日期; E-金额; M-大写金额; ifEmptyDelete: T-值为空删除父节点,默认为F -->  
  5.     <template name="RECOMMEND-LETTER" desc="介绍信" templateFile="template4.xml">  
  6.         <taglist remark="单值标签列表">  
  7.             <tag id="1" name="ToPartment" desc="接收部门" type="S" ifEmptyDelete="T">#ToPartment</tag><!--接收部门-->  
  8.             <tag id="2" name="OwnerName" desc="姓名" type="S">#OwnerName</tag><!--姓名-->  
  9.             <tag id="3" name="CountNum" desc="人数" type="S">#CountNum</tag><!--人数-->  
  10.             <tag id="4" name="Business" desc="内容" type="S">#Business</tag><!--内容-->  
  11.             <tag id="5" name="UsefulDays" desc="有效期" type="S">#UsefulDays</tag><!--有效期-->  
  12.             <tag id="6" name="Year" desc="年" type="S">#Year</tag><!--年-->  
  13.             <tag id="7" name="Month" desc="月" type="S">#Month</tag><!--月-->  
  14.             <tag id="8" name="Day" desc="日" type="S">#Day</tag><!--日-->  
  15.         </taglist>  
  16.     </template>  
  17. </templates>  

 

第三步:编写java代码

 

Java代码   收藏代码
  1. /** 
  2.  * 参数及规则 
  3.  */  
  4. public class RuleDTO {  
  5.     /** 
  6.      * tag名称 
  7.      */  
  8.     private String parmName;  
  9.     /** 
  10.      * tag描述 
  11.      */  
  12.     private String parmDesc;  
  13.     /** 
  14.      * tag序号 
  15.      */  
  16.     private String parmSeq;  
  17.     /** 
  18.      * tag值类型 
  19.      */  
  20.     private String parmType;  
  21.     /** 
  22.      * tag参数名称 
  23.      */  
  24.     private String parmRegular;  
  25.     /** 
  26.      * tag值 
  27.      */  
  28.     private String parmValue;  
  29.       
  30.     /** 
  31.      * tag值为空删除该属性 
  32.      */  
  33.     private String ifEmptyDelete;  

 

Java代码   收藏代码
  1. /** 
  2.  * 描述: Word模板信息 
  3.  */  
  4. public class Template {  
  5.   
  6.     private String name;//模板名  
  7.       
  8.     private String desc;//模板描述  
  9.       
  10.     private String templateFile;//模板文件  
  11.       
  12.     private Vector<RuleDTO> rules;//模板规则  
  13.       
  14. }  

 

Java代码   收藏代码
  1. public class WordBuilder {  
  2.   
  3.     /** 
  4.      * 根据模板读取替换规则 
  5.      * @param templateName  模板ID 
  6.      */  
  7.     @SuppressWarnings("unchecked")  
  8.     public Template loadRules(Map<String, String> ruleValue) {  
  9.         InputStream in = null;  
  10.         Template template = new Template();  
  11.         // 规则配置文件路径  
  12.         String ruleFile = "template-rule.xml";  
  13.   
  14.         // 模板规则名称  
  15.         String templateRuleName = "";  
  16.         try {  
  17.             templateRuleName = ruleValue.get("ruleName");  
  18.             // 读取模板规则文件  
  19.             in = this.getClass().getClassLoader().getResourceAsStream(ruleFile);  
  20.             // 解析模板规则  
  21.             SAXBuilder sb = new SAXBuilder();  
  22.             Document doc = sb.build(in);  
  23.             Element root = doc.getRootElement(); // 得到根元素  
  24.             List<Element> templateList = root.getChildren();// 所有模板配置  
  25.             Element element = null;  
  26.             Vector<RuleDTO> rules = null;  
  27.             for (int i = 0; i < templateList.size(); i++) {// 遍历所有模板  
  28.                 element = (Element) templateList.get(i);  
  29.                 String templateName = element.getAttributeValue("name");  
  30.                 if (templateRuleName.equalsIgnoreCase(templateName)) {// 查找给定的模板配置  
  31.                     template.setName(templateName);  
  32.                     template.setDesc(element.getAttributeValue("desc"));  
  33.                     template.setTemplateFile(element  
  34.                             .getAttributeValue("templateFile"));  
  35.                     List<Element> tagList = ((Element) element.getChildren()  
  36.                             .get(0)).getChildren();// tag列表  
  37.                     Element tag = null;  
  38.                     RuleDTO ruleDTO = null;  
  39.                     rules = new Vector<RuleDTO>();  
  40.                     for (int j = 0; j < tagList.size(); j++) {  
  41.                         tag = (Element) tagList.get(j);  
  42.                         ruleDTO = new RuleDTO();  
  43.                         ruleDTO.setParmName(tag.getAttributeValue("name"));  
  44.                         ruleDTO.setParmDesc("【※"  
  45.                                 + tag.getAttributeValue("desc") + "※】");  
  46.                         ruleDTO.setParmSeq(tag.getAttributeValue("id"));  
  47.                         ruleDTO.setParmType(tag.getAttributeValue("type"));  
  48.                         if ("T".equalsIgnoreCase(tag  
  49.                                 .getAttributeValue("ifEmptyDelete"))) {// 是否可删除标记  
  50.                             ruleDTO.setIfEmptyDelete("T");  
  51.                         } else {  
  52.                             ruleDTO.setIfEmptyDelete("F");  
  53.                         }  
  54.                         ruleDTO.setParmRegular(tag.getText());  
  55.                         // 值  
  56.                         // 判断参数类型  
  57.                         String value = (String) ((Map<String, String>) ruleValue)  
  58.                                 .get(ruleDTO.getParmRegular().replaceAll("#",  
  59.                                         ""));  
  60.                         ruleDTO.setParmValue(value);  
  61.                         rules.add(ruleDTO);  
  62.                     }  
  63.                     template.setRules(rules);  
  64.                     break;  
  65.                 }  
  66.             }  
  67.         } catch (FileNotFoundException e) {  
  68.             e.printStackTrace();  
  69.         } catch (JDOMException e) {  
  70.             e.printStackTrace();  
  71.         } catch (IOException e) {  
  72.             e.printStackTrace();  
  73.         } finally {  
  74.             try {  
  75.                 in.close();  
  76.             } catch (Exception e) {  
  77.                 e.printStackTrace();  
  78.             }  
  79.         }  
  80.         return template;  
  81.     }  
  82.   
  83.     /** 
  84.      * 查找父节点 
  85.      */  
  86.     public Element findElement(Element currNode, String parentNodeId) {  
  87.         // 节点标示为空  
  88.         if (currNode == null || parentNodeId == null) {  
  89.             return null;  
  90.         }  
  91.         Element pNode = null;  
  92.         do {  
  93.             pNode = currNode.getParent();  
  94.             currNode = pNode;  
  95.         } while (parentNodeId.equalsIgnoreCase(pNode.getName()));  
  96.         return pNode;  
  97.     }  
  98.   
  99.     /** 
  100.      * 生成Word文件 
  101.      */  
  102.     @SuppressWarnings("unchecked")  
  103.     public String build(Template template) {  
  104.         InputStream in = null;  
  105.         OutputStream fo = null;  
  106.         // 生成文件的路径  
  107.         String file = "d:\\test\\" + template.getDesc() + ".doc";  
  108.         try {  
  109.             // 读取模板文件  
  110.             in = this.getClass().getClassLoader()  
  111.                     .getResourceAsStream(template.getTemplateFile());  
  112.             SAXBuilder sb = new SAXBuilder();  
  113.             Document doc = sb.build(in);  
  114.             Element root = doc.getRootElement(); // 得到根元素  
  115.             Namespace ns = root.getNamespace();// NameSpace  
  116.             // word 03模板存在<wx:sect>元素  
  117.             List<Element> sectList = root.getChild("body", ns).getChildren();  
  118.             Element sectElement = (Element) sectList.get(0);  
  119.             // <w:p>下的标签集合  
  120.             List<Element> pTagList = sectElement.getChildren("p", ns);  
  121.             // <w:tbl>下的标签集合  
  122.             List<Element> tblTagList = sectElement.getChildren("tbl", ns);  
  123.             if (pTagList != null && pTagList.size() > 0) {  
  124.                 changeValue4PTag(pTagList, template.getRules(), ns, null);  
  125.             }  
  126.             if (tblTagList != null && tblTagList.size() > 0) {  
  127.                 changeValue4TblTag(tblTagList, template.getRules(), ns);  
  128.             }  
  129.             // 写文件  
  130.             XMLOutputter outp = new XMLOutputter(" "true"UTF-8");  
  131.             fo = new FileOutputStream(file);  
  132.             outp.output(doc, fo);  
  133.         } catch (FileNotFoundException e) {  
  134.             e.printStackTrace();  
  135.         } catch (JDOMException e) {  
  136.             e.printStackTrace();  
  137.         } catch (IOException e) {  
  138.             e.printStackTrace();  
  139.         } finally {  
  140.             try {  
  141.                 in.close();  
  142.                 fo.close();  
  143.             } catch (Exception e) {  
  144.                 e.printStackTrace();  
  145.             }  
  146.         }  
  147.         return file;  
  148.     }  
  149.   
  150.     /** 
  151.      * 针对<w:body><wx:sect><w:p>这种层级的WORD模板, 查找及替换<w:p>下的标签。 
  152.      * @param pTagList :<w:p>集合 
  153.      * @param rulesValue :RuleDTO集合 
  154.      * @param ns :NameSpace对象 
  155.      * @param trChildren :<w:tbl>的子节点<w:tr>集合 
  156.      */  
  157.     @SuppressWarnings("unchecked")  
  158.     private boolean changeValue4PTag(List<Element> pTagList,  
  159.             Vector<RuleDTO> rulesValue, Namespace ns, List<Element> trChildren) {  
  160.         Element p = null;  
  161.         boolean delFlag = false;  
  162.         for (int i = 0; i < pTagList.size(); i++) {  
  163.             boolean delCurrNode = false;// 删除当前节点  
  164.             boolean delCurrNode4TabWR = false;// 删除table中单行节点  
  165.             p = (Element) pTagList.get(i);  
  166.             List<Element> pChild = p.getChildren("r", ns);  
  167.             for (int j = 0; pChild != null && j < pChild.size(); j++) {  
  168.                 Element pChildren = (Element) pChild.get(j);  
  169.                 Element t = pChildren.getChild("t", ns);  
  170.                 if (t != null) {  
  171.                     String text = t.getTextTrim();  
  172.                     if (text.indexOf("【※") != -1) {  
  173.                         for (int v = 0; v < rulesValue.size(); v++) {  
  174.                             RuleDTO dto = (RuleDTO) rulesValue.get(v);  
  175.                             if (text.indexOf(dto.getParmDesc().trim()) != -1) {  
  176.                                 // 判断属性值是否为可空删除  
  177.                                 if ("T".equals(dto.getIfEmptyDelete())  
  178.                                         && StringUtils.isBlank(dto  
  179.                                                 .getParmValue())) {  
  180.                                     // 删除该节点顶级节点  
  181.                                     text = "";  
  182.                                     if (trChildren != null) {// 针对<w:tbl>删除该行  
  183.                                         Element element = ((Element) p  
  184.                                                 .getParent()).getParent();  
  185.                                         trChildren.remove(element);  
  186.                                         delCurrNode4TabWR = true;  
  187.                                     } else {// 针对<w:r>删除段  
  188.                                             // pTagList.remove(p);  
  189.                                         pTagList.remove(pChildren);  
  190.                                         delCurrNode = true;  
  191.                                     }  
  192.                                     break;  
  193.                                 } else {  
  194.                                     text = text.replaceAll(dto.getParmDesc()  
  195.                                             .trim(), dto.getParmValue());  
  196.                                 }  
  197.                             }  
  198.                         }  
  199.                         t.setText(text);  
  200.                     }  
  201.                     if (delCurrNode4TabWR) {// <w:tbl>TABLE下的行节点已删除  
  202.                         delFlag = true;  
  203.                         break;  
  204.                     } else if (delCurrNode) {// <w:p>下的节点已删除  
  205.                         i--;  
  206.                         delFlag = true;  
  207.                         break;  
  208.                     }  
  209.                 }  
  210.             }  
  211.         }  
  212.         return delFlag;  
  213.     }  
  214.   
  215.     /** 
  216.      * 针对含有表格的WORD模板, 查找及替换<w:tbl>下的标签。 
  217.      * @param tblTagList :<w:tbl>集合 
  218.      * @param rulesValue :RuleDTO集合 
  219.      * @param ns :NameSpace对象 
  220.      */  
  221.     @SuppressWarnings("unchecked")  
  222.     private void changeValue4TblTag(List<Element> tblTagList,  
  223.             Vector<RuleDTO> rulesValue, Namespace ns) {  
  224.         Element p = null;  
  225.         for (int i = 0; tblTagList != null && i < tblTagList.size(); i++) {  
  226.             p = (Element) tblTagList.get(i);  
  227.             List<Element> trChildren = p.getChildren("tr", ns);  
  228.             for (int j = 0; trChildren != null && j < trChildren.size(); j++) {// 循环<w:tr>  
  229.                 Element pChildren = (Element) trChildren.get(j);  
  230.                 List<Element> tcTagList = pChildren.getChildren("tc", ns);  
  231.                 for (int c = 0; tcTagList != null && c < tcTagList.size(); c++) {// 循环<w:tc>取<w:p>集合  
  232.                     Element tcChildren = (Element) tcTagList.get(c);  
  233.                     List<Element> pTagList = tcChildren.getChildren("p", ns);  
  234.                     boolean delFlag = changeValue4PTag(pTagList, rulesValue,  
  235.                             ns, trChildren);  
  236.                     if (delFlag) {// 删除行后需要改变trChildren的指针位置  
  237.                         j--;  
  238.                     }  
  239.                 }  
  240.             }  
  241.         }  
  242.     }  
  243.   
  244.     public static void main(String[] args) throws Exception {  
  245.         WordBuilder word = new WordBuilder();  
  246.         Map<String, String> map = new HashMap<String, String>();  
  247.         //填充参数  
  248.         map.put("ToPartment""XXX公司");  
  249.         map.put("OwnerName""张三");  
  250.         map.put("CountNum""5");  
  251.         map.put("Business""例行检查");  
  252.         map.put("UsefulDays""15");  
  253.         map.put("Year""2014");  
  254.         map.put("Month""5");  
  255.         map.put("Day""13");  
  256.         map.put("ruleName""RECOMMEND-LETTER");  
  257.         Template template = word.loadRules(map);  
  258.         //直接打开文件  
  259.         Runtime.getRuntime().exec("explorer " + word.build(template));  
  260.     }  
  261. }  

 

 第四步:大功告成

 

几点总结及注意事项:

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自动换行功能(关于模板中换行的方案暂没有想到更好的),则需要首先计算出对应模板该行的字数,然后采用空格填充来实现。

 


目录
相关文章
|
18天前
|
前端开发 JavaScript 安全
前端性能调优:HTTP/2与HTTPS在Web加速中的应用
【10月更文挑战第27天】本文介绍了HTTP/2和HTTPS在前端性能调优中的应用。通过多路复用、服务器推送和头部压缩等特性,HTTP/2显著提升了Web性能。同时,HTTPS确保了数据传输的安全性。文章提供了示例代码,展示了如何使用Node.js创建一个HTTP/2服务器。
31 3
|
23天前
|
移动开发 开发者 HTML5
构建响应式Web界面:Flexbox与Grid的实战应用
【10月更文挑战第22天】随着互联网的普及,用户对Web界面的要求越来越高,不仅需要美观,还要具备良好的响应性和兼容性。为了满足这些需求,Web开发者需要掌握一些高级的布局技术。Flexbox和Grid是现代Web布局的两大法宝,它们分别由CSS3和HTML5引入,能够帮助开发者构建出更加灵活和易于维护的响应式Web界面。本文将深入探讨Flexbox和Grid的实战应用,并通过具体实例来展示它们在构建响应式Web界面中的强大能力。
34 3
|
1月前
|
存储 安全 关系型数据库
后端技术:构建高效稳定的现代Web应用
【10月更文挑战第5天】后端技术:构建高效稳定的现代Web应用
53 1
|
6天前
|
缓存 安全 网络安全
HTTP/2与HTTPS在Web加速中的应用
HTTP/2与HTTPS在Web加速中的应用
|
8天前
|
SQL 安全 前端开发
PHP与现代Web开发:构建高效的网络应用
【10月更文挑战第37天】在数字化时代,PHP作为一门强大的服务器端脚本语言,持续影响着Web开发的面貌。本文将深入探讨PHP在现代Web开发中的角色,包括其核心优势、面临的挑战以及如何利用PHP构建高效、安全的网络应用。通过具体代码示例和最佳实践的分享,旨在为开发者提供实用指南,帮助他们在不断变化的技术环境中保持竞争力。
|
19天前
|
前端开发 安全 应用服务中间件
前端性能调优:HTTP/2与HTTPS在Web加速中的应用
【10月更文挑战第26天】随着互联网的快速发展,前端性能调优成为开发者的重要任务。本文探讨了HTTP/2与HTTPS在前端性能优化中的应用,介绍了二进制分帧、多路复用和服务器推送等特性,并通过Nginx配置示例展示了如何启用HTTP/2和HTTPS,以提升Web应用的性能和安全性。
17 3
|
19天前
|
前端开发 JavaScript API
前端框架新探索:Svelte在构建高性能Web应用中的优势
【10月更文挑战第26天】近年来,前端技术飞速发展,Svelte凭借独特的编译时优化和简洁的API设计,成为构建高性能Web应用的优选。本文介绍Svelte的特点和优势,包括编译而非虚拟DOM、组件化开发、状态管理及响应式更新机制,并通过示例代码展示其使用方法。
33 2
|
19天前
|
测试技术 持续交付 PHP
PHP在Web开发中的应用与最佳实践###
【10月更文挑战第25天】 本文将深入探讨PHP在现代Web开发中的应用及其优势,并分享一些最佳实践来帮助开发者更有效地使用PHP。无论是初学者还是有经验的开发者,都能从中受益。 ###
40 1
|
20天前
|
负载均衡 监控 算法
论负载均衡技术在Web系统中的应用
【11月更文挑战第4天】在当今高并发的互联网环境中,负载均衡技术已经成为提升Web系统性能不可或缺的一环。通过有效地将请求分发到多个服务器上,负载均衡不仅能够提高系统的响应速度和处理能力,还能增强系统的可扩展性和稳定性。本文将结合我参与的一个实际软件项目,从项目概述、负载均衡算法原理以及实际应用三个方面,深入探讨负载均衡技术在Web系统中的应用。
48 2
|
23天前
|
JavaScript 前端开发 持续交付
构建现代Web应用:Vue.js与Node.js的完美结合
【10月更文挑战第22天】随着互联网技术的快速发展,Web应用已经成为了人们日常生活和工作的重要组成部分。前端技术和后端技术的不断创新,为Web应用的构建提供了更多可能。在本篇文章中,我们将探讨Vue.js和Node.js这两大热门技术如何完美结合,构建现代Web应用。
21 4