FreeMarker文档生成技术在毕业设计管理系统中的应用

2017-01-21 15:47周建锋
软件导刊 2016年12期

摘 要:FreeMarker是一种使用Java语言编写的模板引擎框架技术。使用该技术构建了一种模板样式与模型数据相互分离的Word文档动态生成方案。该方案应用于毕业设计管理信息系统,可以在线生成选题审批表、任务书、开题报告、中英文摘要、论文扉页等具有固定结构的Word文档。实践证明,该方案代码量少,文档内容及样式控制方便,保证了毕业设计相关文档格式的高度一致性。

关键词:FreeMarker;模板技术;Word自动生成;毕业设计管理

DOIDOI:10.11907/rjdk.162232

中图分类号:TP319

文献标识码:A文章编号:1672-7800(2016)012-0109-04

0 引言

随着教育信息化的发展,众多高校将计算机技术应用于日常教学管理中[1]。毕业设计(论文)是高校实现人才培养目标的重要实践教学环节,应充分利用先进的信息技术提高管理效率,从而提高毕业设计质量。笔者为天津理工大学中环信息学院设计开发了一个基于B/S架构的毕业设计管理信息系统[2],系统使用Java Web技术实现,采用基于组件式开发的MVC(Model View Controller)设计模式[3],高效实现了毕业设计信息化、规范化管理。系统中涉及的所有基础性数据(如学生信息、指导教师信息、题目信息等)以及毕业设计过程中师生在线填写的各类文档都持久化存储在对应的MySQL数据库表中。但是,诸如选题审批表、任务书、开题报告、提交审核表等文档仍需要打印纸质版后由学生或指导教师签字,这些纸质版文档或者要求院系存档,或者需要装订在毕业设计说明书(论文)中。如何将MySQL中的相关数据导出并生成符合格式规范要求的Word文档,成为毕业设计管理信息系统必须实现的一项功能。本文选用基于FreeMarker模板引擎技术构建了一种模板样式与模型数据相互分离的Word文档动态生成方案,通过较少的代码量就可以满足毕业设计管理信息系统中的文档导出需求。

1 相关技术介绍

1.1 FreeMarker

FreeMarker是使用纯Java编写的一款模板引擎,即一种基于模板、用于生成输出文本(HTML、XML、RTF、Java源代码等)的通用工具[4]。FreeMarker不是面向最终用户的,而是一个Java类库,适合作为嵌入其它开发产品中的一个组件,其最初设计被用来在MVC模式的Web开发框架中生成HTML页面,但它不依赖于 Servlet、HTML或Web环境,仅仅专注于展示数据,与具体的程序逻辑相分离。它的核心原理是模板+数据模型=输出。首先,FreeMarker拥有功能强大的模板语言(FreeMarker Template Language,简称FTL),包含了诸如include、if/elseif/else、循环结构等常用指令,在模板中创建和改变变量,并且能够在任何地方使用复杂表达式来指定值。其次,FreeMarker具有通用的数据模型,它不是直接反射到Java对象,而是通过插件式对象封装,以变量方式在模板中显示Java对象。

1.2 Word对XML的支持

微软从Office Word 2003就开始针对XML进行了完整设计,支持称为Word 标记语言的原生XML词汇,使Word文档可以和XML文档相互转换[5]。开发者可建立这种XML和可编程代码来增强Word文档,并帮助用户建立和修改文档。

常见的WordXML格式标签示例如下:表示Word文档中的段落,和html中的

标签类似;表示一个样式串,指明它包括的文本显示样式,如文本属性加粗、下划线等;表示Word里的字符串,即文字内容;Word段落属性包含在标签中;标签中定义文本格式。

2 系统数据模型

在毕业设计管理信息系统中,具有固定结构和格式规范要求的文档有封皮、扉页、选题审批表、任务书、开题报告、中期检查表、中英文摘要、提交审核表和装订审批表等。这些文档结构和格式固定,其中有一部分文档内容由用户在毕业设计中通过网络在线提交并持久化至系统数据库。本文简要介绍系统数据库,然后以开题报告为例说明如何准备模型数据。

2.1 系统数据库

系统使用MySQL数据库,主要涉及的数据库表有adminuser(管理员表)、teacher(教师表)、stu(学生表)、syear(学年表)、dept(部门表)、major(专业表)、rank(职称表)、ttask(教师任务表)、project(题目信息表)等,其中毕业设计相关文档数据主要来源于project表,结构如表1所示(限于篇幅,仅列出project表中的部分字段)。

2.2 准备模型数据

数据模型并不是文本文件,它来自于Java对象。图1为开题报告的一个Word示例,其中使用矩形方框标出的是生成开题报告需要的动态模型数据。不难发现,这些数据均来源于project关系表,系统与project关系表对应的JavaBean实体类为project,对project关系表进行查询得到的记录将通过ORM(对象关系映射)转换为一个project类的对象实例,该对象的成员变量分别对应project表中各字段。

数据模型是树形的,可以通过HashMap类构建简单的数据模型,例如将某个题目信息对象project以map映射数据("project", project)存入数据模型,在flt模板中则可以通过${project.ptitle}获取毕业设计(论文)标题,通过${project.sname}获取学生姓名,其访问模型数据方式类似于EL表达式。

3 FreeMarker模板准备

在确定了模型数据对象名称、结构和语义之后,才能准确无误地使用模板语言构建动态可变内容。下面以创建开题报告模板为例说明如何准备FreeMarker模板。

3.1 准备Word模板

准备一个如图1所示的Word 2003版本开题报告,需要注意的是开题报告内容比较多,可能会跨页,为保证表格跨页正常显示,需要将开题报告内容所在的单元格设置为允许跨页断行。

3.2 转换为XML并保存为ftl文件

将准备好的开题报告模板另存为Word 2003 XML文档,然后将其后缀名修改为flt即可。

3.3 修改ftl文件,完成数据填充

修改flt文件,将如图1所示的需要动态填充的零散数据替换为ftl语言表达式。文献[6-8]中通过类似${project.sname}方式取出模型数据,完成对模板的填充,但在实际应用中有可能产生两个问题:①当模型数据对象为NULL或者不存在的情况下会抛掉异常;②当模型数据中含有“<”、“>”、“&”、单引号、双引号这些XML本身的标记符号时,可能造成XML文件解析错误,使最终生成的Word文档打不开。为保证程序的健壮性,在应用实践中采用如下方法解决这两个问题:

(1)针对问题①,对可能为NULL的模型数据在引用前先使用ftl语言条件表达式“<#if>”进行判断。

(2)针对问题②,将所有对模型数据的引用置于CDATA区段,由“<![CDATA[”开始,“]] >”结束,因为处在CDATA 区段中的文本不会被XML解析器解析。

以毕业设计(论文)题目为例,题目不可能为NULL,但子标题可能为NULL。另外,题目和子标题中可能存在XML本身使用的标记符号,在模板中对毕业设计(论文)题目可按照以下ftl语言表达式进行替换:<![CDATA[${project.ptitle}]] ><#if project.psubtitle??> <![CDATA[——${project.psubtitle}]] >。

3.4 对多个段落内容的特殊处理

以开题报告内容为例说明。开题报告内容由许多段落组成,由学生在页面中通过textarea文本域填写并提交。学生在填写开题报告内容时通过回车换行进行分段,可能会在换行后输入多个空格来实现类似Word段落首行缩进的效果。最终开题报告内容被提交并存储至数据库project表ktbgnr字段中,其中的回车换行被保存为不可见字符“\\r\\n”(或者“\\n”,或者“\\r”,由不同的操作系统而定)。如果按照前述方法使用<![CDATA[${project.ktbgnr}]] >进行数据填充,只能生成一个段落。为保证能够生成多个格式统一段落,可按下述方法分析符合指定格式要求的段落在XML中如何表示。

在一个空白的Word文档中按照论文段落格式要求(宋体、小四号、多倍行距1.25、段落首行缩进2字符)编写一段内容,然后将其另存为Word 2003 XML格式文档,打开该文档并找到对应段落的XML代码,如图2所示。为叙述方便,将图2中阴影部分对应的XML字符串简记为,矩形方框内对应的XML字符串简记为,那么在ftl模板文件中对应一个指定格式要求的完整段落就由+段落内容+组成。 根据多个段落内容存储特点以及固定格式Word段落的xml结构,设计对多个段落内容的特殊处理方案:

(1)将回车换行符(“\\r\\n”、 “\\n”或“\\r”)替换为统一的分隔字符串,生成一个uuid作为分隔字符串,保证分隔字符串在开题报告内容中不会出现,否则会产生错误的分段信息。

(2)去除分隔字符串前后所有连续的空白字符。

(3)多段内容替换填充,采用如下两种策略:①按照分隔字符串将多个段落内容分隔为一个字符串数组,数组元素表示一个个段落,按照图2所示格式使用ftl循环指令依次输出每个段落,段落内容置于CDATA 区段中;②将分隔字符串替换为“]] ><![CDATA[”,替换后的内容以map映射数据("ktbknr", ktbknr)存入数据模型,然后按图2所示代码格式在ftl模板中完成数据填充:<![CDATA[${ktbknr}]] >。该方法利用了CDATA区段不被解析特性以及ftl模板中的段落组成方式,将开题报告内容中的多个段落巧妙地拼接构造出来,最终生成格式一致的多段内容。

4 程序实现

4.1 创建工具类并初始化FreeMarker配置实例

首先导入版本号为2.3.24的freemarker.jar包,并在com.bs.util包下创建WordUtil.java工具类。使用FreeMarker的第一步是创建配置实例,由于FreeMarker的configuration配置对象全局只需要维护一个,因此在WordUtil.java类中将configuration定义为静态变量并通过静态代码初始化:

private static Configuration configuration;

static {

configuration = new Configuration(Configuration.getVersion());

configuration.setDefaultEncoding("UTF-8");

}

接着在该类中封装对多个段落进行预处理的stringFilter()方法和生成Word文档的creatWord()方法,下面分别对这两个方法进行介绍。

4.2 对多个段落内容的预处理

对多个段落内容的处理采用前述的替换填充策略。在生成选题理由、开题报告内容等包含多个段落内容文档时,需要使用该方法对数据进行预处理,代码如下:

public static String stringFilte(String source) {

String uuid = UUID.randomUUID().toString();

return source.replaceAll("\\r\\n", uuid) //

.replaceAll("\\r|\\n", uuid) //

.replaceAll("\\s*"+uuid+"\\s*", uuid) //

.replace(uuid, "]] ><![CDATA[");

}

4.3 生成Word文档方法

生成Word的方法封装如下,其中省略了try…catch语句块。

public static void createWord(ServletContext servletContext, Map dataMap, String templateName, String filePath, String fileName){

configuration.setServletContextForTemplateLoading(servletContext, "/templates/");

Template template = configuration.getTemplate(templateName);

File outFile = new File(filePath + fileName);

if (!outFile.getParentFile().exists()) {

outFile.getParentFile().mkdirs();

}

Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile), "UTF-8"));

template.process(dataMap, out);

out.flush();

out.close();

}

需要说明的是,ftl模板文件设置为统一存放在站点根目录的“/templates/”路径下,方法调用时需要提供参数为:当前的servletContext上下文、模型数据Map、使用的模板名称、生成Word文档的存放目录以及Word文档名称。若指定的Word文档存放目录不存在,程序会自动创建该目录。

4.4 应用示例

毕业设计各类文档内容提交时间不同,生成对应Word文档的时机也不同。以生成开题报告Word为例,按照系统设计,学生提交开题报告内容后,由指导教师填写审批意见,当提交审批意见成功后自动生成开题报告Word,此后只有指导教师可以修改开题报告相关内容。指导教师在线修改开题报告内容并提交成功后自动重新生成开题报告Word。生成开题报告Word文档的核心代码如下:

String basepath = this.getServletContext().getRealPath("/");

dataMap.put("project", project);

dataMap.put("ktbgnr", WordUtil.stringFilte(project.getKtbgnr()));

dataMap.put("ktbgyj", WordUtil.stringFilte(project.getKtbgyj()));

WordUtil.createWord(this.getServletContext(), dataMap, "ktbg.ftl", basepath + stu.getFpath(), stu.getSno() + "_KaiTiBaoGao.doc");

代码说明:①当前题目project对象和对应学生stu对象已经通过查询得到;②开题报告内容(ktbgnr字段)和开题报告意见(ktbgyj字段)包含多个段落内容,需要进行预处理;③系统最初导入学生信息时,会根据一定的规则自动生成一个唯一的服务器相对路径并保存在fpath字段,此路径下对应存储该学生的所有相关文档,因此生成的Word文档路径由服务器绝对路径basepath + stu.getFpath()指定。

5 结语

本文基于FreeMarker模板引擎构建了Word文档动态生成方案,不仅代码量非常少,而且解决了零散数据填充时容易被忽略的两个问题,增强了程序的健壮性。与此同时,对应用中可能出现的多个段落内容数据填充问题提出了有效的解决方案。在毕业设计管理信息系统的应用实践中,实现了数据导出并生成完全符合特定格式要求的Word文档功能,满足了具有统一格式规范的各类文档导出需求。

参考文献:

[1] 夏松竹.基于B/S结构的本科毕业设计管理信息系统设计与实现[J].工业和信息化教育,2016(1):82-86.

[2] 周建锋,付延友.毕业设计管理信息系统的设计与实现[J].软件导刊, 2016(8):96-98.

[3] ROD JOHNSON.J2EE设计开发编程指南[M].魏海萍,于晓菲,毛选,译.北京:电子工业出版社,2003.

[4] 刘全飞,周相兵.基于FreeMarker的站群系统模板设计实现[J].信息技术, 2015(7):36-39.

[5] 肖斌,李超,汪敏.基于C#快速生成Word报告[J].计算机系统应用,2012,21(7):232-235.

[6] 王庆喜,李源.基于freemarker和XML技术的Word生成方案[J].电脑与信息技术,2012,20(2):31-33.

[7] 曾玉林.FreeMarker在JSP项目中Word报表应用研究[J].电脑开发与应用,2013,26(12):59-61.

[8] 王正敏,张太红,李永可,等.FreeMarker模板引擎在线动态生成Excel和Word文档技术[J].计算机与现代化, 2016(4):109-113.

(责任编辑:杜能钢)