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

 


目录
相关文章
|
17天前
|
前端开发 JavaScript 关系型数据库
从前端到后端:构建现代化Web应用的技术探索
在当今互联网时代,Web应用的开发已成为了各行各业不可或缺的一部分。从前端到后端,这篇文章将带你深入探索如何构建现代化的Web应用。我们将介绍多种技术,包括前端开发、后端开发以及各种编程语言(如Java、Python、C、PHP、Go)和数据库,帮助你了解如何利用这些技术构建出高效、安全和可扩展的Web应用。
|
1月前
|
监控 Serverless 测试技术
Serverless 应用引擎常见问题之做的web服务计费如何解决
Serverless 应用引擎(Serverless Application Engine, SAE)是一种完全托管的应用平台,它允许开发者无需管理服务器即可构建和部署应用。以下是Serverless 应用引擎使用过程中的一些常见问题及其答案的汇总:
407 3
|
1月前
|
前端开发 数据库 UED
构建高性能Web应用的关键技术
本文将介绍构建高性能Web应用的关键技术,包括前端优化、后端优化、数据库优化等方面。通过深入讨论各项技术的原理和实践方法,帮助开发者们提升Web应用的响应速度和用户体验。
|
1天前
|
存储 中间件 Go
探索Gin框架:快速构建高性能的Golang Web应用
探索Gin框架:快速构建高性能的Golang Web应用
|
1天前
|
前端开发 JavaScript Java
前端与后端:构建现代Web应用的双翼
前端与后端:构建现代Web应用的双翼
|
10天前
|
缓存 负载均衡 数据库
优化后端性能:提升Web应用响应速度的关键策略
在当今数字化时代,Web应用的性能对于用户体验至关重要。本文探讨了如何通过优化后端架构和技术手段,提升Web应用的响应速度。从数据库优化、缓存机制到异步处理等多个方面进行了深入分析,并提出了一系列实用的优化策略,以帮助开发者更好地应对日益增长的用户访问量和复杂的业务需求。
15 1
|
10天前
|
缓存 监控 数据库
Flask性能优化:打造高性能Web应用
【4月更文挑战第16天】本文介绍了提升Flask应用性能的七大策略:优化代码逻辑,减少数据库查询,使用WSGI服务器(如Gunicorn、uWSGI),启用缓存(如Flask-Caching),优化数据库操作,采用异步处理与并发(如Celery、Sanic),以及持续监控与调优。通过这些手段,开发者能有效优化Flask应用,适应大型或高并发场景,打造高性能的Web服务。
|
11天前
|
数据库 开发者 Python
Python中使用Flask构建简单Web应用的例子
【4月更文挑战第15天】Flask是一个轻量级的Python Web框架,它允许开发者快速搭建Web应用,同时保持代码的简洁和清晰。下面,我们将通过一个简单的例子来展示如何在Python中使用Flask创建一个基本的Web应用。
|
15天前
|
JavaScript 前端开发 API
Vue.js:构建高效且灵活的Web应用的利器
Vue.js:构建高效且灵活的Web应用的利器
|
23天前
|
XML JSON JavaScript
使用JSON和XML:数据交换格式在Java Web开发中的应用
【4月更文挑战第3天】本文比较了JSON和XML在Java Web开发中的应用。JSON是一种轻量级、易读的数据交换格式,适合快速解析和节省空间,常用于API和Web服务。XML则提供更强的灵活性和数据描述能力,适合复杂数据结构。Java有Jackson和Gson等库处理JSON,JAXB和DOM/SAX处理XML。选择格式需根据应用场景和需求。