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

使用freemarker转word简单教程

程序员文章站 2022-03-12 17:56:35
...

事前准备

由于ftl 文件可能在idea 中报 约束未注册的问题 URI is not registered 请参考下面的博客
链接: idea中xml或者ftl文件xmlns约束报红解决方案.
由于ftl 文件可能在eclipse中缺少freemarker 编译器导致失败的问题 要安装 freemarker ide 请参考下面的博客
链接: eclipse中离线安装 freemarker IDE 插件和jad反编译插件.
编辑ftl文件时常用的freemarker语法 请参考下面的博客
链接: freemarker ftl文件简单语法整理.

下载jar包

freemarker-2.3.30.jar
博客这两天上传附件老是失败,就不放在博客里了,有需要可以给我留言,或者自行搜索下载

编辑一个word模板

如图所示,一些要传参的地方可以提前使用freemarker的ftl语法,加入变量
使用freemarker转word简单教程

模板处理

将编辑好的word转存为xml 格式,使用notepad++ 或者 ue打开
这是代码都是一行的比较乱,使用idea中 Ctrl+Alt+L 进行格式化,
或者 notepad++中的 插件 -> XML Tools-> pretty print(XML only with line breaks),或者直接快捷键 Ctrl alt shift B,进行格式化
编译之后每一行的标签是<w:tr>
同时注意对于列表中
要使用
表格循环用标签
<#list userList as user>
<w:tr>
姓名:user.userName{user.userName} , 性别:{user.sex}
</w:tr>
</#list>
使用freemarker转word简单教程
使用freemarker转word简单教程
再就是如果有图片会看到图片占位的地方,有一片base64编码后的代码,把base64替换成${image}

	<pkg:part pkg:name="/word/media/image1.jpg" pkg:contentType="image/jpeg" pkg:compression="store">
		<pkg:binaryData>${imagej}</pkg:binaryData>
	</pkg:part>
	<pkg:part pkg:name="/word/media/image2.PNG" pkg:contentType="image/png" pkg:compression="store">
		<pkg:binaryData>${imagep}</pkg:binaryData>
	</pkg:part>

在此过程中可以将创建人,文件生成时间等一些属性删去也是没有问题的
最后,更改文件后缀为 。.ftl

代码实现

工具处理类

WordUtil

package com.yucheng.cmis.util;

import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;

import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateExceptionHandler;
import freemarker.template.Version;
import sun.misc.BASE64Encoder;

public class WordUtil {

    // FreeMarker 配置
    private static Configuration cfg;
    //默认的给定公共文件路径
    private static String basePath = System.getProperty("user.dir")+"/WebContent/WEB-INF/template";

    /**
     * @author JL-J 静态内部类,创建freeMarker 配置,实例化工具类
     */
    private static class LazyHolder {

        // 创建 freeMarker 配置 ,2.3.30  引入的freemarker 版本号
        private static final Configuration config = new Configuration(new Version("2.3.30"));

        // 实例化工具类
        private static final WordUtil wu = new WordUtil();
    }

    /**
     * 私有构造函数
     */
    private WordUtil() {

    }

    /**
     * 初始化配置文件,获取实例
     *
     * @param templatePath 模板路径
     * @return FreeMarkerUtil 工具类
     */
    public static WordUtil getInstance() {

        if (null == cfg) {

            // 创建 freeMarker 配置
            cfg = LazyHolder.config;

            // 设置编码格式
            cfg.setDefaultEncoding("UTF-8");
            // templatePath 指的是模板所在路径
        }
//  这是旧方式        
//        TemplateLoader templateLoader;
//		try {
//			templateLoader = new FileTemplateLoader(new File(basePath));
//			
//			cfg.setTemplateLoader(templateLoader);
//		} catch (IOException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
		
        try {
        	//使用文件路径加载模板
            cfg.setDirectoryForTemplateLoading(new File(basePath));
        } catch (IOException e) {
            e.printStackTrace();
        }
		
		//使用类路径在载模板
        //cfg.setClassForTemplateLoading(WordUtil.class, basePath);
      // 设置异常处理器,这样的话就可以${as.df}, 即使没有对应属性也不会出错
		cfg.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);

        return LazyHolder.wu;
    }

    /**
     * 根据模版名称加载对应模版
     *
     * @param templateName 模版名称
     * @return
     */
    private Template getTemplate(String templateName) {

        try {

            return cfg.getTemplate(templateName);
        }
        catch (IOException e) {

            e.printStackTrace();
        }

        return null;
    }

    /**
     * 控制台打印通过模板生成的文件
     *
     * @param dataModel 数据模型
     * @param templateName 输出模版
     */
    public String getContent(Map<String, Object> dataModel, String templateName) {

        try {

            StringWriter stringWriter = new StringWriter();

            getTemplate(templateName).process(dataModel, stringWriter);

            stringWriter.flush();

            String result = stringWriter.toString();

            stringWriter.close();

            return result;
        }
        catch (TemplateException e) {

            e.printStackTrace();
        }
        catch (IOException e) {

            e.printStackTrace();
        }

        return null;
    }

    /**
     * 创建通过模板生成的文件
     *
     * @param dataModel 数据模型
     * @param templateName 输出模版
     * @param filePath 输出文件路径   "/WebContent/WEB-INF/template"  下   /word/test.doc
     */
    public File create(Map<String, Object> dataModel, String templateName, String filePath) {

        try {
        	filePath = basePath + filePath;
            File file = new File(filePath);

            if (!file.getParentFile().exists()) {

                file.getParentFile().mkdirs();
            }

            BufferedWriter bufWrite = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filePath),"UTF-8"));
            getTemplate(templateName).process(dataModel, bufWrite);
            bufWrite.flush();
            bufWrite.close();

            return file;
        }
        catch (TemplateException e) {

            e.printStackTrace();
        }
        catch (IOException e) {

            e.printStackTrace();
        }

        return null;
    }

    /**
     * 根据地址获得数据的字节流
     *
     * @param strUrl 网络连接地址
     * @return 图片Base64码
     */
    public String getImgBase64ByUrl(String strUrl) {

        try {

            // 建立 Http 链接
            HttpURLConnection conn = (HttpURLConnection) new URL(strUrl).openConnection();

            // 5秒响应超时
            conn.setConnectTimeout(5 * 1000);
            conn.setDoInput(true);

            // 判断http请求是否正常响应请求数据,如果正常获取图片 Base64 码
            if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {

                // 获取图片输入流
                InputStream inStream = conn.getInputStream();

                // 用于存储图片输出流
                ByteArrayOutputStream outStream = new ByteArrayOutputStream();

                // 定义缓存流,用于存储图片输出流
                byte[] buffer = new byte[1024];

                int len = 0;

                // 图片输出流循环写入
                while ((len = inStream.read(buffer)) != -1) {

                    outStream.write(buffer, 0, len);
                }

                // 图片输出流转字节流
                byte[] btImg = outStream.toByteArray();

                inStream.close();
                outStream.flush();
                outStream.close();

                return new String(Base64.encode(btImg));
            }
        }
        catch (Exception e) {

            e.printStackTrace();
        }

        return null;
    }
    
    
      //根据绝对路径获得图片的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);
    }
	
}

package com.yucheng.cmis.util;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;



public class WordTest {
	public static void main(String[] args) {
	   String fileName = "/word/wer/test.doc";
       String ftlName = "wer/teset.ftl";
       Map<String, Object> dataMap = new HashMap<String, Object>();
       dataMap.put("title","这就是我填的标题");
       List<Map<String,Object>> list = new ArrayList<>();
       for (int i = 0; i < 3; i++) {
    	   Map<String, Object> map = new HashMap<String, Object>();
    	   map.put("name", "何时何地是");
    	   map.put("age", 234);
    	   map.put("address", "暗杀第三方公司的规划");
    	   map.put("cd", "爱上对方");
    	   map.put("df", "afgsdg");
    	   list.add(map);
       }
       dataMap.put("list",list);
       dataMap.put("time","2020-04-19");
		try {
			dataMap.put("imagej",WordUtil.getImageBase("D:/用户目录/Downloads/博客附件/24.jpg"));
			dataMap.put("imagep",WordUtil.getImageBase("D:/用户目录/Downloads/博客附件/2.PNG"));
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	
       WordUtil util = WordUtil.getInstance();
       File file = util.create(dataMap, ftlName, fileName);
       System.out.println(file.getName());
	}
}

//如果是要下载的话 使用 servlet 中的request 和 response
根据上面的代码最后拿到 File 之后,对file进行IO流的下载

 InputStream inputStream = null;
 ServletOutputStream out = null;
 try {
         File file = util.create(dataMap, ftlName, fileName);
        inputStream = new FileInputStream(file);
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/msword");
        response.addHeader("Content-Disposition", "attachment;filename "+file.getName()+");
        out = response.getOutputStream();
        byte[] buffer = new byte[1024]; // 缓冲区
        int bytesToRead = -1;
        // 通过循环将读入的Word文件的内容输出到浏览器中
        while ((bytesToRead = inputStream.read(buffer)) != -1) {
        out.write(buffer, 0, bytesToRead);
        }
    } catch (Exception e) {
        // TODO: handle exception
    } finally {
        if (inputStream != null)
        inputStream.close();
        if (out != null)
        out.close();
        if (file != null)
        file.delete();
    }
    }

结果

使用freemarker转word简单教程