SpringMVC中使用FreeMarker生成Word文档

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文件:

这里写图片描述

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

这里写图片描述

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

这里写图片描述

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

这里写图片描述

建议使用工具格式化一下,也方便我们之后修改那些占位符被拆开的问题,不然我们一个个查找是很繁琐的一件事(个人使用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中,运行

这里写图片描述

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

打开下载的word文件,和原本word文档进行比较一下
这里写图片描述

自此本实例结束!

  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值