• 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=""名称相应,代码例如以下:

      

    <?

    xml version="1.0" encoding="GB2312"?

    > <!-- 模板定义 --> <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>

    第三步:编写java代码

    /**
     * 參数及规则
     */
    public class RuleDTO {
    	/**
    	 * tag名称
    	 */
    	private String parmName;
    	/**
    	 * tag描写叙述
    	 */
    	private String parmDesc;
    	/**
    	 * tag序号
    	 */
    	private String parmSeq;
    	/**
    	 * tag值类型
    	 */
    	private String parmType;
    	/**
    	 * tag參数名称
    	 */
    	private String parmRegular;
    	/**
    	 * tag值
    	 */
    	private String parmValue;
    	
    	/**
    	 * tag值为空删除该属性
    	 */
    	private String ifEmptyDelete;
    
    }
    
    /**
     * 描写叙述: Word模板信息
     */
    public class Template {
    
    	private String name;//模板名
    	
    	private String desc;//模板描写叙述
    	
    	private String templateFile;//模板文件
    	
    	private Vector<RuleDTO> rules;//模板规则
    		
    }


    public class WordBuilder {
    
    	/**
    	 * 依据模板读取替换规则
    	 * @param templateName  模板ID
    	 */
    	@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自己主动换行功能(关于模板中换行的方案暂没有想到更好的)。则须要首先计算出相应模板该行的字数。然后採用空格填充来实现。

  • 相关阅读:
    如何划分与组织内存(上)?
    CountDownLatch和CyclicBarrier:如何让多线程步调一致?
    怎样用读写锁快速实现一个缓存?
    并发容器的前世今生是怎样的?
    java的原子类到底是啥?ABA,CAS又是些什么?
    设置工作模式与环境(中):建造二级引导器
    希尔排序
    order by是怎样工作的?
    如何用信号量去实现一个限流器?
    读多写少的场景下,竟然还有比读写锁更牛X的锁?
  • 原文地址:https://www.cnblogs.com/blfshiye/p/5258477.html
Copyright © 2020-2023  润新知