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

在做项目的过程中,有时候需要把数据生成word文档,常用的有POI、ITEXT等,在这篇文章中我们使用freemarker模板来生成我们想要的word文档。
首先我们需要使用office word来编辑一个word模板文档:
这里写图片描述
上面的图片中,我们把字段字写成${XXX}的格式,这对应这我们实体类的字段,下面的图片可以任意上传两张图片,因为我们会生成base64格式的一段编码。
接下来我们另存word文档为xml文件,打开后如下所示:
这里写图片描述

图中选中的部分如果发现被发开了,不是${XXX}的格式,只需把中间的标签删除即可,接下来是图片部分,我们能看到它生成了一段base64位的编码,我们只需要先删除这段编码,然后把图片对应的字段名以上述的格式写进去即可:
这里写图片描述
接下来是java后台代码,建了一个包放我们编辑好的ftl文件:
这里写图片描述

下面的代码是图片编码转换器,这里写的是固定路径,实际项目中可以从数据库查询保存的路径放入imgFile即可:

 /图片编码转换
    public String getImgStr() throws Exception{
//      System.out.println("realpath=================:::::::::::"+realpath);
//      System.out.println("photo=================:::::::::::"+photo);
        String imgFile = "D:\timg.png";
        InputStream in = null;
        byte[] data = null;
        in = new FileInputStream(imgFile);
        data = new byte[in.available()];
        in.read(data);
        in.close();
        BASE64Encoder ecoder = new BASE64Encoder();
        return ecoder.encode(data);
    }      

下面是主要代码,它主要用来读取到ftl文件,然后放入我们对应字段的值:

private Configuration configuration = null;

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

    public void createWord() {
        Map<String, Object> dataMap = new HashMap<String, Object>();
        try {
            dataMap.put("name", "张三");
            dataMap.put("sex", "男");
            dataMap.put("age", "22");
            dataMap.put("school", "清华大学");
            dataMap.put("age", "22");
            dataMap.put("sfzzm", getImgStr());
            dataMap.put("sfzfm", getImgStr2());

            configuration.setClassForTemplateLoading(this.getClass(), "/template"); //
            Template template = configuration.getTemplate("demo.ftl");

            File outFile = new File("D:/wordTest.doc");//
            Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile), "UTF-8"));
            template.process(dataMap, out);
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

Template类是主要使用的模板引擎类。接下来写个测试方法,就会在我们指定的d盘下面生成一个叫wordTest.doc的word。

public static void main(String[] args) {
        WordTest test = new WordTest();
        test.createWord();
    }

打开后生成word文档如下:
这里写图片描述

到此使用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的代码就结束了。

发布了21 篇原创文章 · 获赞 8 · 访问量 4367

猜你喜欢

转载自blog.csdn.net/qq_34365173/article/details/80874576