使用freemarker生成word文档(包含遍历多条数据、图片)

最近需要做一个导出word的功能, 在网上搜了下, 有用POI,JXL,iText等jar生成一个word文件然后将数据写到该文件中,API非常繁琐而且拼出来的样式也不美观,于是选择了另一种方式----feemarker基于word模板的导出方式, 这种方式非常简单而且导出的样式美观, 其原理就是先做一个word模板, 该模板中变量数据用${xxx}替换即可。

实现步骤:

一 、新建一个需要导出样式的word文档,并将里面动态数据使用${xxx}替换

在这里插入图片描述

二、将该word文件另存为xml格式(注意是另存为,不是直接改扩展名)

在这里插入图片描述

三 、将xml文件的扩展名直接改为ftl

在这里插入图片描述

四、用java代码完成导出(需要导入freemarker.jar)

接下来是java后台代码,建了一个包放我们编辑好的ftl文件
在这里插入图片描述
生成word工具类

package com.sgd.eic.common.utils;
import freemarker.template.Configuration;
import freemarker.template.Template;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.Map;

/**
 * @Author mxg
 * @Date 2020/3/1 9:23
 **/
public class WordUtil {
    private static final String FTL_FP = "/templates/"; //模板路径
    private static Configuration configuration = null;
    static{
        configuration = new Configuration();
        configuration.setDefaultEncoding("utf-8");//设置默认的编码

    }

    public static Boolean writeWordReport(String wordFilePath, String wordFileName, String templateFileName, Map<String, Object> beanParams, HttpServletResponse response) {
        Writer out = null;
        try {
            configuration.setClassForTemplateLoading(WordUtil.class,FTL_FP);
            Template template = configuration.getTemplate(templateFileName, "UTF-8");

            //获取文件目录,如果不存在则创建
            String filePath = "";
            int index = wordFilePath.lastIndexOf(File.separator);
            if(index != wordFilePath.length()-1){
                filePath = wordFilePath+ File.separator;
            }else {
                filePath = wordFilePath;
            }
            File file1 = new File(filePath);
            if(!file1.exists()){
                file1.mkdirs();
            }

            //输出文件
            File file = new File(filePath+wordFileName);
            FileOutputStream fos = new FileOutputStream(file);
            out = new OutputStreamWriter(fos, "UTF-8");
            template.process(beanParams, out);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }finally{
            try {
                if(out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

处理层方法
   /**
      * @Author maixiaogang
      * @Date 2020/3/1 16:27
      * 生成报告请求
     **/
    @RequestMapping("/word")
    public void generateWord(HttpServletResponse response){
       
        dataMap.put("A2001", "333");
        dataMap.put("A2002", "替换内容");
        //文件生成路径
        String wordFilePath = "D:\\ftl";
        //文件生成名称(因为是2003版本的xml模板,这里使用.doc后缀,如果使用.docx后缀生成的文件有问题)
        String wordFileName = "text.doc";
        //模板文件名称
        String templateFileName = "test1.ftl";

        //生成word文档
        Boolean result = WordUtil.writeWordReport(wordFilePath, wordFileName, templateFileName, dataMap, response);
    }

到此使用freemarker生成word就结束了,由于我们再实际的项目中,下载word会调用浏览器的下载功能进行下载,所以下面贴下处理方法:

// 提示:在调用工具类生成Word文档之前应当检查所有字段是否完整    
        // 否则Freemarker的模板引擎在处理时可能会因为找不到值而报错 这里暂时忽略这个步骤了    
        File file = null;    
        InputStream fin = null;    
        ServletOutputStream out = null;    
        try {    
            // 调用工具类WordUtil的createDoc方法生成Word文档    
            file = WordUtil.createDoc(map, "userDoc");    
            fin = new FileInputStream(file);    
            String fileName = gagl.getSname()+"的沙盘档案报告.doc";
            response.setCharacterEncoding("utf-8");    
            response.setContentType("application/msword");    
            // 设置浏览器以下载的方式处理该文件默认名为resume.doc    
//          response.addHeader("Content-Disposition", "attachment;filename=userDoc.doc");  
            response.setHeader("Content-Disposition", "attachment;filename="  
                     .concat(String.valueOf(URLEncoder.encode(fileName, "UTF-8"))));  

            out = response.getOutputStream();    
            byte[] buffer = new byte[1024];  // 缓冲区    
            int bytesToRead = -1;    
            // 通过循环将读入的Word文件的内容输出到浏览器中    
            while((bytesToRead = fin.read(buffer)) != -1) {    
                out.write(buffer, 0, bytesToRead);    
            }    
        } finally {    
            if(fin != null) fin.close();    
            if(out != null) out.close();    
            if(file != null) file.delete(); // 删除临时文件    
        }
        return null; 

接下来就是遍历,如果我们的图片路径放在一个字段里,我们肯定需要分割然后再显示图片,数据也是一样,如果我们的数据有多条,需要遍历之后都显示在word中,那么该怎么做呢,下面简单的介绍下,这部分就不做实例了,直接贴代码:

//已完成技能培训
        String skillStrs2 ="";
        List<String> skillList = new ArrayList<String>();
        String[] skillStrs = teacher.getSkillTheme().split(",");
        for(int i=0;i<skillStrs.length;i++){
            skillStrs2 = skillStrs[i];
            skillList.add(skillStrs2);
        }
        map.put("skillThemeList", skillList);//技能主题

        String hourStrs2 = "";
        List<String> hourList =  new ArrayList<String>();
        String[] hourStrs = teacher.getSkillHours().split(",");
        for(int i=0;i<hourStrs.length;i++){
            hourStrs2 = hourStrs[i];
            hourList.add(hourStrs2);
        }
        map.put("skillHoursList", hourList);  //技能总计小时数
        //技能培训证书照
        try {
            String simgStrs2 ="";
            List<String> skillImgList = new ArrayList<String>();
            String[] simgStrs = teacher.getSkillImg().split(",");
            for(int i=0;i<simgStrs.length;i++){
                simgStrs2 = simgStrs[i].substring(14, simgStrs[i].length());
                try {
                    skillImgList.add(getImgStr(simgStrs2));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            map.put("skillImgList", skillImgList);
        } catch (Exception e) {
            e.printStackTrace();
        }

面的代码中,第一部分是数据的集合,下面是图片集合,我们先进行分割,然后遍历后放入集合里,最后再放入我们需要的map里,这个map对应的key就是ftl里对应的${}字段。

然后我们需要去修改我们的ftl文件:

<#list supervisorTypeList as sups>
<#list supervisorHoursList as shours>
<#list supervisorImgList as supim>
<#assign a=sups_index /> 
<#assign b=shours_index /> 
<#assign c=supim_index /> 
<#if (a=b) >
<#if (b=c) >
<w:p wsp:rsidR="00551926" wsp:rsidRDefault="00551926" wsp:rsidP="003D2B62"><w:pPr><w:jc w:val="left"/><w:rPr><w:rFonts w:ascii="宋体" w:h-ansi="宋体"/><wx:font wx:val="宋体"/><w:sz w:val="28"/><w:sz-cs w:val="28"/></w:rPr></w:pPr><w:r><w:rPr><w:rFonts w:ascii="宋体" w:h-ansi="宋体" w:hint="fareast"/><wx:font wx:val="宋体"/><w:sz w:val="28"/><w:sz-cs w:val="28"/></w:rPr><w:t>心理督导</w:t></w:r><w:r wsp:rsidR="00E90F59"><w:rPr><w:rFonts w:ascii="宋体" w:h-ansi="宋体" w:hint="fareast"/><wx:font wx:val="宋体"/><w:sz w:val="28"/><w:sz-cs w:val="28"/></w:rPr><w:t></w:t></w:r></w:p>
<w:p wsp:rsidR="00A57CB2" wsp:rsidRDefault="00551926" wsp:rsidP="003D2B62"><w:pPr><w:jc w:val="left"/><w:rPr><w:rFonts w:ascii="宋体" w:h-ansi="宋体"/><wx:font wx:val="宋体"/><w:sz w:val="28"/><w:sz-cs w:val="28"/></w:rPr></w:pPr><w:r><w:rPr><w:rFonts w:ascii="宋体" w:h-ansi="宋体" w:hint="fareast"/><wx:font wx:val="宋体"/><w:sz w:val="28"/><w:sz-cs w:val="28"/></w:rPr><w:t>1.</w:t></w:r><w:r wsp:rsidR="00DE7BE6"><w:rPr><w:rFonts w:ascii="宋体" w:h-ansi="宋体" w:hint="fareast"/><wx:font wx:val="宋体"/><w:sz w:val="28"/><w:sz-cs w:val="28"/></w:rPr><w:t>督导类型:${sups}</w:t></w:r></w:p>
<w:p wsp:rsidR="00DE7BE6" wsp:rsidRDefault="00551926" wsp:rsidP="003D2B62"><w:pPr><w:jc w:val="left"/><w:rPr><w:rFonts w:ascii="宋体" w:h-ansi="宋体"/><wx:font wx:val="宋体"/><w:sz w:val="28"/><w:sz-cs w:val="28"/></w:rPr></w:pPr><w:r><w:rPr><w:rFonts w:ascii="宋体" w:h-ansi="宋体" w:hint="fareast"/><wx:font wx:val="宋体"/><w:sz w:val="28"/><w:sz-cs w:val="28"/></w:rPr><w:t>2.</w:t></w:r><w:r wsp:rsidR="002B42DF" wsp:rsidRPr="002B42DF"><w:rPr><w:rFonts w:ascii="宋体" w:h-ansi="宋体" w:hint="fareast"/><wx:font wx:val="宋体"/><w:sz w:val="28"/><w:sz-cs w:val="28"/></w:rPr><w:t>总计小时数</w:t></w:r><w:r wsp:rsidR="002B42DF"><w:rPr><w:rFonts w:ascii="宋体" w:h-ansi="宋体" w:hint="fareast"/><wx:font wx:val="宋体"/><w:sz w:val="28"/><w:sz-cs w:val="28"/></w:rPr><w:t>:${shours}</w:t></w:r></w:p>
<w:p wsp:rsidR="002B42DF" wsp:rsidRDefault="00551926" wsp:rsidP="003D2B62"><w:pPr><w:jc w:val="left"/><w:rPr><w:rFonts w:ascii="宋体" w:h-ansi="宋体"/><wx:font wx:val="宋体"/><w:sz w:val="28"/><w:sz-cs w:val="28"/></w:rPr></w:pPr><w:r><w:rPr><w:rFonts w:ascii="宋体" w:h-ansi="宋体" w:hint="fareast"/><wx:font wx:val="宋体"/><w:sz w:val="28"/><w:sz-cs w:val="28"/></w:rPr><w:t>3.</w:t></w:r><w:r wsp:rsidR="00A97706"><w:rPr><w:rFonts w:ascii="宋体" w:h-ansi="宋体" w:hint="fareast"/><wx:font wx:val="宋体"/><w:sz w:val="28"/><w:sz-cs w:val="28"/></w:rPr><w:t>证书</w:t></w:r><w:r><w:rPr><w:rFonts w:ascii="宋体" w:h-ansi="宋体" w:hint="fareast"/><wx:font wx:val="宋体"/><w:sz w:val="28"/><w:sz-cs w:val="28"/></w:rPr><w:t>照片</w:t></w:r><w:r wsp:rsidR="00A97706"><w:rPr><w:rFonts w:ascii="宋体" w:h-ansi="宋体" w:hint="fareast"/><wx:font wx:val="宋体"/><w:sz w:val="28"/><w:sz-cs w:val="28"/></w:rPr><w:t></w:t></w:r></w:p><w:p wsp:rsidR="00A97706" wsp:rsidRDefault="00A86982" wsp:rsidP="003D2B62"><w:pPr><w:jc w:val="left"/><w:rPr><w:rFonts w:ascii="宋体" w:h-ansi="宋体"/><wx:font wx:val="宋体"/><w:sz w:val="28"/><w:sz-cs w:val="28"/></w:rPr></w:pPr><w:r wsp:rsidRPr="00A86982"><w:rPr><w:rFonts w:ascii="宋体" w:h-ansi="宋体"/><wx:font wx:val="宋体"/><w:noProof/><w:sz w:val="28"/><w:sz-cs w:val="28"/></w:rPr>
<w:pict>
      <v:shapetype id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f">
       <v:stroke joinstyle="miter"/>
       <v:formulas>
        <v:f eqn="if lineDrawn pixelLineWidth 0"/>
        <v:f eqn="sum @0 1 0"/>
        <v:f eqn="sum 0 0 @1"/>
        <v:f eqn="prod @2 1 2"/>
        <v:f eqn="prod @3 21600 pixelWidth"/>
        <v:f eqn="prod @3 21600 pixelHeight"/>
        <v:f eqn="sum @0 0 1"/>
        <v:f eqn="prod @6 1 2"/>
        <v:f eqn="prod @7 21600 pixelWidth"/>
        <v:f eqn="sum @8 21600 0"/>
        <v:f eqn="prod @7 21600 pixelHeight"/>
        <v:f eqn="sum @10 21600 0"/>
       </v:formulas>
       <v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect"/>
       <o:lock v:ext="edit" aspectratio="t"/>
      </v:shapetype>
      <w:binData w:name="${"wordml://0200001"+supim_index+1+".jpg"}" xml:space="preserve">${supim}</w:binData>
      <v:shape id="图片" o:spid="_x0000_i1025" type="#_x0000_t75" style="width:414.75pt;height:207.75pt;visibility:visible;mso-wrap-style:square">
       <v:imagedata src="${"wordml://0200001"+supim_index+1+".jpg"}" o:title="菜单"/>
      </v:shape>
     </w:pict>
</w:r></w:p>
</#if>
</#if>
</#list>
</#list>
</#list>

上述的代码就是在ftl中使用遍历了,便签都是固定的,有兴趣的可以尝试下遍历,然后显示在word中。由于需要生成ftl文件,如果字段过多的话,写起来就会很麻烦,看到那么多标签,改起来也是个大工程。这应该是一个弊端。好了,至此freemarker生成word的代码就结束了。

其他福利
微信扫下方二维码关注公众号,经常分享一些技术上的理解文章。欢迎骚扰,还可以回复想要的管理系统或者毕业设计,分享各种系统源码(仅限Java语言哦,如需定制系统加qq:226186862)
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿麦小七

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值