一个JAVA小项目--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="年"名称对应,代码如下:
<!--?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;//模板规则
}</ruledto>
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));
}
}</string,></string,></element></w:p></w:tc></element></w:tr></element></ruledto></element></w:tbl></w:tbl></w:p></w:tbl></w:r></w:tbl></element></element></ruledto></element></w:tr></w:tbl></w:p></w:p></w:p></wx:sect></w:body></element></w:tbl></element></w:p></element></wx:sect></string,></ruledto></element></ruledto></element></string,>
第四步:大功告成
几点总结及注意事项:
1. 定义的元素name必须与template_rule.xml中对应相同的name的值一致,否则需要设置转换规则。
2. 模板xml中定义的占位符【※※】中的文字必须与template_rule.xml中对应的desc相同,否则需要设置转换规则.
3. 在配置好模板xml后,需要检查标签下的子节点是否是标签(与word版本有关),如果没有,则必须加上该标签。
4. 如果要动态删除标签节点,则这个节点的内容需要在模板中的同一行,如果不是,则可以手动调整模板xml。
5. 如果需要实现word自动换行功能(关于模板中换行的方案暂没有想到更好的),则需要首先计算出对应模板该行的字数,然后采用空格填充来实现。
上一篇: 详解springboot 使用c3p0数据库连接池的方法
下一篇: 老生常谈Python进阶之装饰器