Fork me on GitHub

Java使用FreeMarker自动生成Word文档

在项目开发中有一需求要将采集的数据填入word表格中,效果如下。本文记录了使用freemaker完成此功能的过程。

1、创建word模板

使用word创建名为template.docx的文档,内容如下:

说明:

  1. 模板文档中${}是占位符,即生成Word文档时占位符会被真实的数据替换。例如${name}在生成文档时会被name这个属性的值替换 ,${info.title}在生成文档时会被info这个对象的title属性的值替换
  2. 如果要在word中包含图片,由于要在生成的Word文档中自动插入一张图片,因此,需要在模板文档中插入一张图片作为占位符。

2、转换为模板文档

使用Word将模板文档另存为Word XML 文档(*.xml)格式,转换后生成template.xml模板文档。使用文本编辑器打开,可以借助xml在线格式化工具(http://www.bejson.com/otherformat/xml/)进行格式化。

打开格式化后的.xml文档可以发现,Word在转换时会自动的将占位符分开,因此需要把占位符之间多余的部分删除掉。如下:

修改为如下:

模板文档在转换成xml格式时,图片的内容会被转换成很长的16进制的字符串。将《pkg:binaryData》《/pkg:binaryData》标签中16进制字符串形式的图片内容替换成${info.image}占位符。

如果需要循环遍历添加内容,则需要使用如下标签:<#list userList as user> </#list>

经过上述处理后,将.xml模板文档进行保存,并直接修改后缀名为ftl,即:template.ftl。

3、创建java程序

  1. 引入maven依赖

    <dependency>
                <groupId>org.freemarker</groupId>
                <artifactId>freemarker</artifactId>
                <version>2.3.28</version>
    </dependency>
    
  2. 工具类如下:

    import freemarker.template.Configuration;
    import freemarker.template.Template;
    import freemarker.template.Version;
    import java.io.BufferedWriter;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.OutputStreamWriter;
    import java.io.Writer;
    import java.util.Map;
    
    /**
     * Word文档工具类
     */
    public class WordUtil {
    
        /**
         * 使用FreeMarker自动生成Word文档
         * @param dataMap   生成Word文档所需要的数据
         * @param fileName  生成Word文档的全路径名称
         */
        public static void generateWord(Map<String, Object> dataMap, String fileName) throws Exception {
            // 设置FreeMarker的版本和编码格式
            Configuration configuration = new Configuration(new Version("2.3.28"));
            configuration.setDefaultEncoding("UTF-8");
    
            // 设置FreeMarker生成Word文档所需要的模板的路径
            configuration.setDirectoryForTemplateLoading(new File("E:/Word/Template/"));
            // 设置FreeMarker生成Word文档所需要的模板
            Template t = configuration.getTemplate("WordTemplate.ftl", "UTF-8");
            // 创建一个Word文档的输出流
            Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(fileName)), "UTF-8"));
            //FreeMarker使用Word模板和数据生成Word文档
            t.process(dataMap, out);
            out.flush();
            out.close();
        }
    }
    
    import org.springframework.util.StringUtils;
    import sun.misc.BASE64Encoder;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    
    /**
     * 图片工具类
     */
    public class ImageUtil {
    
        /**
         * 将图片内容转换成Base64编码的字符串
         * @param imageFile 图片文件的全路径名称
         * @return 转换成Base64编码的图片内容字符串
         */
        public static String getImageBase64String(String imageFile) {
            if (StringUtils.isEmpty(imageFile)) {
                return "";
            }
            File file = new File(imageFile);
            if (!file.exists()) {
                return "";
            }
            InputStream is = null;
            byte[] data = null;
            try {
                is = new FileInputStream(file);
                data = new byte[is.available()];
                is.read(data);
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            BASE64Encoder encoder = new BASE64Encoder();
            return encoder.encode(data);
        }
    }
    
  3. 启动类如下:

    public static void main(String[] args) throws Exception {
            /**
             * 自动生成Word文档
             * 注意:生成的文档的后缀名需要为doc,而不能为docx,否则生成的Word文档会出错
             */
            WordUtil1.generateWord(getWordData(), "E:/User44.doc");
        }
    
        /**
         * 获取生成Word文档所需要的数据
         */
        private static Map<String, Object> getWordData() {
            /*
             * 创建一个Map对象,将Word文档需要的数据都保存到该Map对象中
             */
            Map<String, Object> dataMap = new HashMap<>();
            DataInfo info = new DataInfo();
            //.....
            info.setContent("心的儿子就是福气。\n");
            infoList.add(info);
            DataInfo info2 = new DataInfo();
            //....
            infoList.add(info2);
            dataMap.put("infoList", infoList);
            return dataMap;
        }
    

4、参考文章

https://blog.csdn.net/weixin_44516305/article/details/88049964

https://blog.csdn.net/qq_39507330/article/details/80350596

注:暂未解决循环插入多张图片的问题。

posted @ 2021-08-09 14:04  ayueC  阅读(1667)  评论(0)    收藏  举报