freemark导出word全过程 + 图片不显示问题

开发流程说明

1.按照客户要求来编辑“绘制”所需word文档

使用word绘制出客户需要的那种结果,让客户确认结果,然后再制作。freemark导出word全过程 + 图片不显示问题_第1张图片

2.绘制完成后,另存为《Word 2003 XML 文档(*.xml)》

确认无误后,另存
freemark导出word全过程 + 图片不显示问题_第2张图片

3.将保存后的文件的后缀名改为《.ftl》

freemark导出word全过程 + 图片不显示问题_第3张图片

4.使用小红本打开ftl文件,复制全部内容,然后去百度在线格式化xml文件

打开这个ftl文件,然后复制里面的所有内容,去百度格式化一下
freemark导出word全过程 + 图片不显示问题_第4张图片
格式化网址: http://www.bejson.com/otherformat/xmlsort/freemark导出word全过程 + 图片不显示问题_第5张图片

5.将格式化后的内容覆盖到.ftl 文件里面,然后把这个文件放到开发目录中

将内容覆盖到zkz.ftl文件里面freemark导出word全过程 + 图片不显示问题_第6张图片

6.引入freemark的jar包,web项目自己去百度下载个,maven项目直接引入

传统的web项目引入2个jar包:

freemarker-2.3.28.jar
sun.misc.BASE64Decoder.jar

maven项目这样写即可:


<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-freemarkerartifactId>
dependency>
<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-webartifactId>
dependency>

7.引入freemark工具,来生成对应的word文件

import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateExceptionHandler;
import sun.misc.BASE64Encoder;

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

 
 /*******************************************
  *word文档导出
  *******************************************/
 public class FreeMarkerUtil {
 
     private static Logger log = Logger.getLogger(FreeMarkerUtil.class.toString());
     private static final String ENCODING = "UTF-8";
     private static Configuration cfg = new Configuration();
 
     //初始化cfg
     static {
         //我们要将刚刚创建成的ftl格式的文件放到这个路径下
         cfg.setClassForTemplateLoading(FreeMarkerUtil.class, "/templates/XXXXX/freemaker");
         // setEncoding这个方法一定要设置国家及其编码,不然在ftl中的中文在生成html后会变成乱码
         cfg.setEncoding(Locale.getDefault(), ENCODING);
         // 设置对象的包装器
         cfg.setObjectWrapper(new DefaultObjectWrapper());
         // 设置异常处理器,这样的话就可以${a.b.c.d}即使没有属性也不会出错
         cfg.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);
 
     }
 
     //获取模板对象
     public static Template getTemplate(String templateFileName) throws IOException {
         return cfg.getTemplate(templateFileName, ENCODING);
     }
 
     /**
      * 据数据及模板生成文件
      * @param data             Map的数据结果集
      * @param templateFileName ftl模版文件名
      * @param outFilePath      生成文件名称(可带路径)
      */
     public static File createFile(Map<String, Object> data, String templateFileName, String outFilePath) {
         Writer out = null;
         File outFile = new File(outFilePath);
         try {
             // 获取模板,并设置编码方式,这个编码必须要与页面中的编码格式一致
             Template template = getTemplate(templateFileName);
             if (!outFile.getParentFile().exists()) {
                 outFile.getParentFile().mkdirs();
             }
             out = new OutputStreamWriter(new FileOutputStream(outFile), ENCODING);
             // 处理模版
             template.process(data, out);
             out.flush();
             log.info("由模板文件" + templateFileName + "生成" + outFilePath + "成功.");
         } catch (Exception e) {
             log.error("由模板文件" + templateFileName + "生成" + outFilePath + "出错");
             e.printStackTrace();
         } finally {
             try {
                 if (out != null) {
                     out.close();
                 }
             } catch (IOException e) {
                 log.error("关闭Write对象出错", e);
                 e.printStackTrace();
             }
         }
         return outFile;
     }
 
     //获得图片的base64码
     public static String getImageBase(String src) throws Exception {
         if (src == null || src == "") {
             return "";
         }
         File file = new File(src);
         if (!file.exists()) {
             return "";
         }
         InputStream in = null;
         byte[] data = null;
         try {
             in = new FileInputStream(file);
             data = new byte[in.available()];
             in.read(data);
             in.close();
         } catch (IOException e) {
             e.printStackTrace();
         }
         BASE64Encoder encoder = new BASE64Encoder();
         return encoder.encode(data);
     }
 
     public static void main(String[] args) {
         try {
             Map<String, Object> data = new HashMap<String, Object>();
             SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
	         Date date=new Date();
	         String current=sdf.format(date);
	         data.put("dqn", current.substring(0,4));
             createFile(data, "zhuohao.ftl", "C:/Users/Administrator/Desktop/test.doc");
         } catch (Exception e) {
             e.printStackTrace();
         }
     }
 }

8.通过工具方法将数据写入.ftl文件,然后导出到word中

js前端携带查询表单的查询参数(与列表的数据要一致)到后台获取数据
js代码:

goToWord_zkz: function(){
    var self = this;
    window.location.href = baseUrl + "xxxCont/xxxxxForWord?dclx=zkzdy&pcid=" + self.queryForm.pcid
        + "&xmtab="+self.queryForm.xmtab
        +"&zytab="+self.queryForm.zytab
        + "&sftcs=" + self.queryForm.sftcs
        + "&bklb=" + self.queryForm.bklb
        + "&sfxstp="+self.queryForm.sfxstp;
    self.$Message.success({
        background: true,
        duration: 10,
        content: "如果数据较多,请耐心等待,不要重复点击"
    });
},

java代码:

/**
 * 打印导出
 * @param dclx 导出类型:zhdy桌号打印、dzddy对照单打印、zkzdy准考证打印
 */
@RequestMapping(value = "xxxxxForWord")
public void xxxxxForWord(HttpServletRequest request, HttpServletResponse response, HttpSession session,Model model, ZkzwhEntity zzkzwhEnt, String dclx) throws IOException {
    Map<String, Object> data = new HashMap<String, Object>();
    data.put("currNd", DateUtil.CurrentTime("yyyy"));

    List<ZkzwhEntity> xslist = zkzwhService.queryXsList_word(zzkzwhEnt);//new PageInfo(zkzwhService.queryXsList(bkshPage));
    data.put("xslist", xslist);

    // 先下载到服务器
    String outPath = File.separator + "upload" + File.separator + "freeMaker" + File.separator + "temp";
    File fl1 = new File(context.getRealPath(outPath));
    fl1.mkdirs();
    File fl2 = new File(context.getRealPath(outPath), user.getUsername()+dclx+".doc");// 下载到服务器的文件名称
    if(!fl2.exists()) {
        fl2.createNewFile();
    }

    String ftlName = "";//模板文件的全称
    String docName = "";//导出文件的名字
    FreeMarkerUtil fmu = new FreeMarkerUtil();
    if("zhdy".equals(dclx)){//桌号打印
        ftlName = "zhuohao.ftl";
        docName = "年单招桌贴";
    }else if("dzddy".equals(dclx)){//对照单打印
        ftlName = "duizhaodan.ftl";
        docName = "年单招对照单";
    }else if("zkzdy".equals(dclx)){//准考证打印
        ftlName = "zhunkaozheng.ftl";
        docName = "年单招准考证";
    }
    fmu.createFile(data, ftlName, context.getRealPath(outPath) + File.separator + user.getUsername()+dclx + ".doc");

    // 读取文件
    if(fl2.exists()) {
        InputStream ins = new FileInputStream(fl2);
        BufferedInputStream bins = new BufferedInputStream(ins);// 放到缓冲流里面
        OutputStream outs = response.getOutputStream();// 获取文件输出IO流
        BufferedOutputStream bouts = new BufferedOutputStream(outs);
        response.setContentType("application/x-download");// 设置response内容的类型
        response.setHeader("Content-disposition" , "attachment;filename=" + new String((DateUtil.CurrentTime("yyyy")+ docName + ".doc").getBytes("GB2312"), "ISO-8859-1"));// 设置头部信息
        int bytesRead = 0;
        byte[] buffer = new byte[8192];
        // 开始向网络传输文件流
        while ((bytesRead = bins.read(buffer, 0, 8192)) != -1) {
            bouts.write(buffer, 0, bytesRead);
        }
        bouts.flush();// 这里一定要调用flush()方法
        ins.close();
        bins.close();
        outs.close();
        bouts.close();
    } else {
        response.sendRedirect("下载失败");
    }
}

9.遇到的问题,图片不展示

开发过程中,一直都很顺利很快乐,但是最后的最后,还是遇到了问题,就是模板循环出来后,图片没有打印出来。
freemark导出word全过程 + 图片不显示问题_第7张图片
图片上的保存信息,意思就是图片资源没有找到。
百度了下,百度到如下信息,即id重复的问题,于是根据百度到的内容,我做出了修改:

<w:binData w:name="wordml://src_${xsObj.sfzh}.png" xml:space="preserve">${xsObj.zp_str!}w:binData>
<v:shape id="图片_${xsObj_index-1}" o:spid="_x0000_s1026${xsObj_sfzh}" type="#_x0000_t75${xsObj.sfzh}">
    <v:imagedata src="wordml://src_${xsObj.sfzh}.png" o:title=""/>
v:shape>

注意:
1,${xsObj.zp_str!} 传递的是我从后台取出来的base格式的图片内容。
2,<\binData> 标签之间不能有其他任何字符,类似于textarea标签的性质一样。
3,word中存在多张 不同 的图片,那么在遍历图片时,id和name必须不同才可以,使用循环的下标来标记或者数据库中的id来标记,像我的文章中,我使用了身份证号作为id,就可以不重复。

我以为这就大功告成了,但是实际上,还是不行,我以为是我哪里写错了,我仔细检查了好几遍,确定代码没有写错,于是开始找数据的问题。
结果却是发现是数据来源的问题。
word转成的xml再转成ftl格式的数据后,它不支持前缀为 “data:image/jpg;base64,”,就是因为我的bas格式的图片中,我在存储时,故意拼接了这个字符,为了方便页面中 直接显示图片。但是在这里就必须没有这个 “data:image/jpg;base64,” 开头才可以。
因为别的地方还用到这个base格式的图像,所以不方便修改保存方法,于是在数据库中查询的时候截取了一下字符串即可:
freemark导出word全过程 + 图片不显示问题_第8张图片
freemark导出word全过程 + 图片不显示问题_第9张图片
完美!真香。

你可能感兴趣的:(踩过的坑,实用完整小功能,POI)