FreeMarker生成Word文档

时间:2023-12-20 08:56:44

FreeMarker简介: 
FreeMarker是一款模板引擎:即一种基于模板和要改变的数据,并用来生成输出文本(HTML网页、电子邮件、配置文件、源代码等)的通用工具,它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。 
FreeMarker是免费的,基于Apache许可证2.0版本发布。其模板编写为FreeMarker Template Language(FTL),数据简单、专用的语言。需要准备数据在真实编程语言中来显示,比如数据库查询和业务运算,之后模板显示已经准备好的数据。在模板中,主要用于如何展示数据,而在模板之外注意于要展示什么数据。

SpringMVC是啥在这就不介绍了。

这实例主要是完成使用FreeMarker在SpringMVC框架中生成word文档下载。

1、实例是maven工程,工程对jar包的依赖,pom.xml中主要的依赖是对springMVC和freemarker的包依赖:

<properties>
<!-- spring版本号 -->
<spring.version>3.2.8.RELEASE</spring.version>
<!-- junit版本号 -->
<junit.version>4.10</junit.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- freemarker依赖 -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.23</version>
</dependency>
<!-- 添加Spring依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<!--spring单元测试依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<!-- jstl标签支持 -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- Servlet核心包 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<!--JSP -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
</dependencies>

2、SringMV配置 
applicationContext.xml的配置,这里没有其他复杂的配置,jdbc等,主要为测试FreeMarker的整合:

<!-- 配置自动扫描的包 -->
<context:component-scan base-package="com.zhihua.controller.*"/> <context:annotation-config/>

web.xml配置:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name></display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list> <listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener> <context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:applicationContext.xml</param-value>
</context-param> <servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping> <!-- 配置编码方式过滤器 ,注意一点,要配置所有的过滤器,最前面 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>false</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>

接下来是SpringMVC整合FreeMarker最主要的配置了,spring-mvc.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!-- springmvc 注解驱动 -->
<mvc:annotation-driven />
<context:component-scan base-package="com.zhihua.*" /> <!-- 访问静态资源 -->
<mvc:default-servlet-handler /> <!-- 配置试图解析器 -->
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/"></property>
<!-- 后缀 -->
<property name="suffix" value=".jsp"></property>
<property name="order" value="1"/>
</bean>
<import resource="classpath:applicationContext.xml" /> <!-- FreeMarker配置 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.freemarker.FreeMarkerView"/>
<property name="contentType" value="text/html; charset=utf-8"/>
<property name="cache" value="true" />
<property name="suffix" value=".ftl" />
<property name="order" value="0"/><!-- 配置视图解析的顺序 -->
</bean>
<bean id="freeMarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<!-- 模板的根目录 -->
<property name="templateLoaderPath" value="/static/templates/"/><!-- freemark模板存放的位置 -->
<!-- 编码格式 -->
<property name="defaultEncoding" value="utf-8"/><!-- freemark编码格式 -->
<property name="freemarkerSettings">
<props>
<prop key="template_update_delay">1</prop><!--刷新模板的周期,单位为秒 -->
</props>
</property>
</bean> </beans>

3、maven工程的目录,其中com.zhihua.templates包中存放的模板文件,例如本实例中的resume.ftl文件:

FreeMarker生成Word文档

4、我们所需要的模板.ftl文件是从哪里来的呢?(本例中的resume.ftl)其中很简单,FreeMarker是模板引擎,所以我们首先要一个模板,生成word文档当然也不例外,我们先新建一个word文件,然后把需要的改变的数据用类似${xxxx}形式当做占位符。

FreeMarker生成Word文档

把该word文档另存为XML文件:

FreeMarker生成Word文档

另存后建议用用Editplus、Notepad++、Sublime等工具打开查看一下,因为有的时候你写的占位符可能会被拆开(可能会出现类似为${xxxx, xxxxx.... .}形式的拆分,要修改成${}形式),用文件工具打开后,可能回出现未格式化的情况

FreeMarker生成Word文档

建议使用工具格式化一下,也方便我们之后修改那些占位符被拆开的问题,不然我们一个个查找是很繁琐的一件事(个人使用Notepad++ XML格式化插件,这个百度一下有插件安装方式)。

5、好了,前期的准备工作都完成了,现在开始功能的实现了。 
工具类代码:

package com.zhihua.util;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map; import freemarker.template.Configuration;
import freemarker.template.Template; public class Resume_Word { private static Configuration configuration = null;
private static Map<String,Template> allTemplate = null; static{
configuration = new Configuration(Configuration.VERSION_2_3_0);
configuration.setDefaultEncoding("UTF-8");
configuration.setClassForTemplateLoading(Resume_Word.class, "/com/zhihua/templates");
// try {
// configuration.setDirectoryForTemplateLoading(new File("E:\\"));
// } catch (IOException e1) {
// e1.printStackTrace();
// }
allTemplate = new HashMap<String,Template>();
try{
allTemplate.put("test", configuration.getTemplate("resume.ftl"));
}catch(IOException e){
e.printStackTrace();
throw new RuntimeException(e);
}
} private Resume_Word(){
} public static File createDoc(Map<?,?> dataMap,String type){
String name = "temp"+(int)(Math.random()*100000)+".doc";
File f = new File(name);
Template t = allTemplate.get(type);
try{
//这个地方不能使用FileWriter因为需要指定编码类型否则声场的word文档会因为有无法识别的编码而无法打开
Writer w = new OutputStreamWriter(new FileOutputStream(f),"utf-8");
t.process(dataMap,w);
w.close();
}catch(Exception e){
e.printStackTrace();
throw new RuntimeException(e);
}
return f;
}
}

封装的实体类:

package com.zhihua.entity;

public class Resume {

    private String name;//姓名
private String sex;//性别
private String birthday;//出生日期
private String photo;//照片
private String native_place;//籍贯
private String nation;//民族
private String ps;//身体状况
private String graduate_school;//毕业学校
private String political_status;//政治面貌
private String height;//身高
private String foreign;//外语程度
private String education;//学历
private String business;//曾任职位
private String profession;//专业
private String speciality;//特长
private String graduation_time;//毕业时间
private String phone;//联系电话
private String postalcode;//邮政编码
private String address;//家庭地址
private String personal_website;//个人网站
private String email;//E—mail
private String course;//主修课程
private String resume;//个人简历
private String technology;//熟悉技术
private String characteristic;//个人特点
private String ability;//应聘岗位及个人能力
private String practice;//社会实践经历 public Resume(){ }
//省略getting和setting方法....
....... }

controller代码:

package com.zhihua.controller;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map; import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping; import com.zhihua.entity.Resume;
import com.zhihua.entity.User;
import com.zhihua.util.Resume_Word;
import com.zhihua.util.WordGenerator; @Controller
@RequestMapping("/freemarker")
public class FreeMarkerController { //测试freeMarker
@RequestMapping("/index.html")
public String Index(Model model){
model.addAttribute("message","freemark这是主页");
return "hello";
} @RequestMapping("/downResumeDoc")
public String downResumeDoc(HttpServletRequest request,HttpServletResponse response,Resume resume)
throws IOException{
request.setCharacterEncoding("utf-8");
Map<String,Object> map = new HashMap<String,Object>();
/*Enumeration<String> paramNames = request.getParameterNames();
//通过循环将表单参数放入键值对映射中
while(paramNames.hasMoreElements()){
String key = paramNames.nextElement();
String value = request.getParameter(key);
map.put(key, value);
}*/
//给map填充数据
map.put("name",resume.getName());
map.put("sex", resume.getSex());
map.put("native_place",resume.getNative_place());
map.put("nation", resume.getNation());
map.put("ps", resume.getPs());
map.put("birthday",resume.getBirthday()); map.put("photo","1");
map.put("political_status",resume.getPolitical_status());
map.put("height",resume.getHeight());
map.put("foreign",resume.getForeign());
map.put("graduate_school",resume.getGraduate_school());
map.put("education",resume.getEducation()); map.put("business",resume.getBusiness());
map.put("profession",resume.getProfession());
map.put("speciality",resume.getSpeciality());
map.put("phone",resume.getPhone());
map.put("graduation_time",resume.getGraduation_time());
map.put("address",resume.getAddress()); map.put("postalcode",resume.getPostalcode());
map.put("personal_website",resume.getPersonal_website());
map.put("email",resume.getEmail());
map.put("course",resume.getCourse());
map.put("resume",resume.getResume());
map.put("technology",resume.getTechnology()); map.put("characteristic",resume.getCharacteristic());
map.put("ability",resume.getAbility());
map.put("practice",resume.getPractice()); //提示:在调用工具类生成Word文档之前应当检查所有字段是否完整
//否则Freemarker的模板殷勤在处理时可能会因为找不到值而报错,这里暂时忽略这个步骤
File file = null;
InputStream fin = null;
ServletOutputStream out = null; try{
//调用工具类WordGenerator的createDoc方法生成Word文档
file = Resume_Word.createDoc(map, "resume");
fin = new FileInputStream(file); response.setCharacterEncoding("utf-8");
response.setContentType("application/msword");
response.addHeader("Content-Disposition", "attachment;filename=resume.doc"); out = response.getOutputStream();
byte[] buffer = new byte[1024];//缓冲区
int bytesToRead = -1;
// 通过循环将读入的Word文件的内容输出到浏览器中
while((bytesToRead = fin.read(buffer)) != -1) {
out.write(buffer, 0, bytesToRead);
} }catch(Exception ex){
ex.printStackTrace();
}
finally{
if(fin != null) fin.close();
if(out != null) out.close();
if(file != null) file.delete(); // 删除临时文件
}
return null;
}
}

JSP页面代码:

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>"> <title>My JSP 'downDoc.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<style type="text/css">
* {
font-family: "微软雅黑";
} .textField {
border: none;
/* border-bottom: 1px solid gray; */
text-align: center;
} #file {
border: 1px solid black;
width: 80%;
margin: 0 auto;
} h1 input {
font-size: 72px;
} td textarea {
font-size: 14px;
} .key {
width: 125px;
font-size: 20px;
}
.photo{
border: 1px solid black;
width: 80%;
margin: 0 auto;
}
</style>
</head> <body>
<form action='<c:url value="/freemarker/downResumeDoc"/>' method="post">
<div id="file" align="center">
<h1><input type="text" class="textField" value="个人简历"/></h1>
<hr/>
<table style="border-collapse:separate; border-spacing:0px 10px;" id="searchTable" border="1">
<tr>
<td class="key">姓&nbsp;&nbsp;名:</td>
<td><input type="text" name="name" class="textField"/></td>
<td class="key">性&nbsp;&nbsp;别:</td>
<td><input type="text" name="sex" class="textField"/></td>
<td class="key">出生日期:</td>
<td><input type="text" name="birthday" class="textField"/></td>
<td rowspan="3" width="150px" align="center">
<a href="" src="">上传照片</a>
</td>
</tr>
<tr>
<td class="key">籍&nbsp;&nbsp;贯:</td>
<td><input type="text" name="native_place" class="textField"/></td>
<td class="key">民族:</td>
<td><input type="text" name="nation" class="textField"/></td>
<td class="key">身体状况:</td>
<td><input type="text" name="ps" class="textField"/></td>
</tr>
<tr>
<td class="key">政治面貌:</td>
<td><input type="text" name="political_status" class="textField"/></td>
<td class="key">身高:</td>
<td><input type="text" name="height" class="textField"/></td>
<td class="key">外语程度:</td>
<td><input type="text" name="foreign" class="textField"/></td>
</tr>
<tr>
<td class="key">所在学院:</td>
<td><input type="text" name="graduate_school" class="textField"/></td>
<td class="key">学历:</td>
<td><input type="text" name="education" class="textField"/></td>
<td class="key">曾任职务:</td>
<td colspan="2"><input type="text" name="business" class="textField"/></td>
</tr>
<tr>
<td class="key">所学专业:</td>
<td colspan="2"><input type="text" name="profession" class="textField"/></td>
<td class="key" colspan="2">特长:</td>
<td colspan="2"><input type="text" name="speciality" class="textField"/></td>
</tr>
<tr>
<td class="key">毕业时间:</td>
<td colspan="2"><input type="text" name="graduation_time" class="textField"/></td>
<td class="key" colspan="2">联系电话:</td>
<td colspan="2"><input type="text" name="phone" class="textField"/></td>
</tr>
<tr>
<td rowspan="3" class="key">家庭住址:</td>
<td rowspan="3" colspan="2">
<textarea rows="8" cols="35" name="address"></textarea>
</td>
<td colspan="2" class="key">邮政编码:</td>
<td colspan="2"><input type="text" name="postalcode" class="textField"/></td>
</tr>
<tr>
<td colspan="2" class="key">个人网站:</td>
<td colspan="2"><input type="text" name="personal_website" class="textField"/></td>
</tr>
<tr>
<td colspan="2" class="key">E-mail:</td>
<td colspan="2"><input type="text" name="email" class="textField"/></td>
</tr>
<tr>
<td class="key">主修课程:</td>
<td colspan="6"><input type="text" name="course" class="textField"/></td>
</tr>
<tr>
<td class="key">个人简历:</td>
<td colspan="6"><input type="text" name="resume" class="textField"/></td>
</tr>
<tr>
<td class="key">熟悉领域:</td>
<td colspan="6"><input type="text" name="technology" class="textField"/></td>
</tr>
<tr>
<td class="key">个人特点:</td>
<td colspan="6"><input type="text" name="characteristic" class="textField"/></td>
</tr>
<tr>
<td class="key" colspan="2">应聘岗位及个人特长和能力:</td>
<td colspan="5"><input type="text" name="ability" class="textField"/></td>
</tr>
<tr>
<td class="key" colspan="2">社会实践经历:</td>
<td colspan="5"><input type="text" name="practice" class="textField"/></td>
</tr>
<tr>
<td colspan="7" style="text-align: center;">
相信您的信任与我的实力将为我们带来共同的成功! 希望我能为咱们公司贡献自己的力量!
</td>
</tr>
</table>
</div>
<div align="center" style="margin-top:15px;">
<input type="submit" value="保存Word文档" />
</div>
</form>
</body>
</html>

因为resume.ftl文件中的内容太多了,为了避免篇幅过大,这里就不贴出来了,如果按照前面的步骤,获得resume.ftl不难的。

6、代码部分都准备好了, 接下来就是演示部分 
将该工程发布到tomcat中,运行

FreeMarker生成Word文档

填写好全部信息(说一下我碰到的问题,我发现如果.ftl文件中的所有占位符如果没有全部填充数据的话,freemarker不能解析出来我们的想要的结果,后台会报错,改占位符的为值为null或者是miss,这是不允许的!) 
FreeMarker生成Word文档

打开下载的word文件,和原本word文档进行比较一下 
FreeMarker生成Word文档

自此本实例结束!原文地址:https://blog.csdn.net/hundan_520520/article/details/54848165