欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

借助freemarker实现word模板复制功能

程序员文章站 2022-04-30 09:25:22
...

业务场景

导出所有报名用户的报名表,每个用户报名单在word文档中单独一页,方便打印。

报名单模板如下:
借助freemarker实现word模板复制功能

需求:一份word文档,包含所有用户的报名单,每个用户单独一页。

实现思路

  1. 将报名单内容设置成模板,其中需求提到每个用户要单独一页,所以在模板的后面加上换页效果
  2. 一份word文档里面要包含多个用户报名信息,所以模板要重复使用,只是替换用户信息
  3. 将word模板转成xml文件,获取内容信息的模板格式
  4. 将xml转成ftl格式,支持el表达式,支持循环,很好的实现需求

具体实现

  1. 用工具(word/wps)创建好模板的word文档
  2. 将word文档另存为XML文件,命名为apply.xml,打开xml文件,如果是压缩后的XML内容,直接在网上将其格式化,方便阅读和修改
  3. 将XML中需要替换的内容使用EL表达式代替,如:${name}。PS:可以在创建模板时直接使用EL表达式,但是注意在转换成XML的时候可能会存在某些意外,如果不熟悉使用。
  4. 业务需要多个报名单,每份报名单格式都是一样的,所以在XML内容的一开始就加上循环遍历的代码:<#list recordList as record>…..

部分XML内容

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<?mso-application progid="Word.Document"?>
<w:wordDocument xmlns:aml="http://schemas.microsoft.com/aml/2001/core"
    xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"
    xmlns:o="urn:schemas-microsoft-com:office:office"
    xmlns:v="urn:schemas-microsoft-com:vml"
    xmlns:w10="urn:schemas-microsoft-com:office:word"
    xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml"
    xmlns:wx="http://schemas.microsoft.com/office/word/2003/auxHint"
    xmlns:wsp="http://schemas.microsoft.com/office/word/2003/wordml/sp2"
    xmlns:sl="http://schemas.microsoft.com/schemaLibrary/2003/core"
    w:macrosPresent="no" w:embeddedObjPresent="no" w:ocxPresent="no"
    xml:space="preserve">

    <w:body>
      <#list recordList as record>

        ...... 此处省略部分内容

        <w:t>姓名</w:t>

        ...... 此处省略部分内容

        <w:t>${record.name}</w:t>

        ...... 此处省略部分内容

        </#list>
    </w:body>
</w:wordDocument>

JAVA代码

控制器代码:

/**
 * 导出报名列表
 * 
 * @param request
 * @param response
 * @throws IOException
 */
@RequestMapping(value = "/manage/zsh/apply/record/export-word")
public void exportWord(HttpServletRequest request, HttpServletResponse response) throws IOException {
    request.setCharacterEncoding("UTF-8");
    String path = "";
    String fileName = new String("报名表.doc".getBytes("UTF-8"), "iso-8859-1");
    path = buildWordUrl(request, response);

    response.setCharacterEncoding("utf-8");
    response.setContentType("application/msword");
    response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
    File file = new File(path);
    InputStream inputStream = new FileInputStream(file);
    OutputStream os = response.getOutputStream();
    byte[] b = new byte[10240];
    int length;
    while ((length = inputStream.read(b)) > 0) {
        os.write(b, 0, length);
    }
    inputStream.close();
    os.flush();
    os.close();
    file.delete();
}

/**
 * word文档地址
 * 
 * @param request
 * @param response
 * @return
 */
private String buildWordUrl(HttpServletRequest request, HttpServletResponse response) {
    String outDocPath = request.getSession().getServletContext().getRealPath("/") + "/download/"
            + System.currentTimeMillis() + ".doc";
    FreemarkerToWord freemarkerToWord = new FreemarkerToWord();
    freemarkerToWord.createWord(request, outDocPath, "/template", "apply.min.ftl");
    return outDocPath;
}

核心代码:


import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import freemarker.core.ParseException;
import freemarker.template.Configuration;
import freemarker.template.MalformedTemplateNameException;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateNotFoundException;

/**
 * 借助freemarker构建word文档
 * 
 * @author 黄国渝
 * @version 1.0.0
 */
public class FreemarkerToWord {

    private Configuration configuration = null;

    public FreemarkerToWord() {
        configuration = new Configuration(Configuration.VERSION_2_3_23);
        configuration.setDefaultEncoding("UTF-8");
    }

    public void createWord(HttpServletRequest request, String outDocPath, String templatePath, String tmplateName) {
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("recordList", getData());
        // 模板获取方式有很多种,因为我的模板放在部署中间件的WebContent中,所以采用以下方式
        configuration.setServletContextForTemplateLoading(request.getSession().getServletContext(), templatePath);
        try {
            // 注释的内容是旧的写法,会导致乱码
            // configuration.getTemplate(tmplateName);
            configuration.getTemplate(tmplateName, "UTF-8");
            Template t = configuration.getTemplate(tmplateName);
            File outFile = new File(outDocPath);
            // 注释的内容是旧的写法,会导致乱码
            // Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile)));
            Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile), "UTF-8"));
            t.process(dataMap, out);
        } catch (TemplateNotFoundException e2) {
            e2.printStackTrace();
        } catch (MalformedTemplateNameException e2) {
            e2.printStackTrace();
        } catch (ParseException e2) {
            e2.printStackTrace();
        } catch (IOException e2) {
            e2.printStackTrace();
        } catch (TemplateException e) {
            e.printStackTrace();
        }
    }

    private List<Map<String, Object>> getData() {
        // 测试数据
        List<Map<String, Object>> rsult = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            Map<String, Object> dataMap = new HashMap<>();
            dataMap.put("title", "标题");
            dataMap.put("subTitle", "副标题");

            dataMap.put("name", "name" + i);
            dataMap.put("sex", "sex");

            dataMap.put("cardNo", "cardNo");
            dataMap.put("address", "address");
            dataMap.put("tel", "tel");
            dataMap.put("level", "level");
            dataMap.put("school", "school");
            dataMap.put("buildNo", "buildNo");
            dataMap.put("guardian", "guardian");
            dataMap.put("relation", "relation");
            dataMap.put("guardianTel", "guardianTel");
            dataMap.put("program", "program");
            dataMap.put("content", "content");

            dataMap.put("date", "2018年4月5日");
            rsult.add(dataMap);
        }
        return rsult;

    }

}

freemarker的模板写法有很多种,可以参考gtlishujie的Freemarker提供了3种加载模板目录的方法
注意:这次实现方式导出来的word在移动端直接打开,会显示XML格式的内容,目前没有代码层面的解决方案。手动解决方案就是在电脑上打开,然后另存为doc或者docx格式的文档,在移动端就可以使用。