使用freemarker,导出制作好的ftl模板,并写入数据

这篇具有很好参考价值的文章主要介绍了使用freemarker,导出制作好的ftl模板,并写入数据。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

使用freemarker,导出制作好的ftl模板,并写入数据

一、背景

1.1 项目背景

  • 最近在开发一个项目,需要导出一些数据,然后写入到word文档中,然后再导出到本地,这个需求是比较常见的,但是我在网上找了很多资料,都没有找到一个比较好的解决方案,所以就自己写了一个,这里分享给大家,希望能帮助到大家。
  • 项目中使用的技术栈:freemarker
  • 项目中使用的依赖:
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.28</version>
</dependency>

二、实现

2.1 代码实现

  • 代码实现比较简单,就是先制作好ftl模板,然后在模板中标记好需要写入的数据的位置,然后在代码中将数据写入到模板中,然后再导出到本地,具体代码如下:
  • 1.首先创建一个ftl模板,这里我创建了一个test.ftl模板,模板中的内容如下:
<html>
<head>
  <title>Welcome!</title>
</head>
<body>
  <h1>Welcome ${user}!</h1>
  <p>We have there animals:
  <ul>
  <list animals as animal>
    <li>${animal.name} for ${animal.price} Euros
  </list>
  </ul>
  <include "common_footer.html">
</body>
</html>
  • 2.然后在查询数据中定义数据对象,使用@ExportWordField注解标记需要写入的数据,具体代码如下:
@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ExportWordField {

    //字段名称
    String fieldName() default "";

    //是否是文件名称字段,只有导出zip时才会用到
    boolean isFileNameFiled() default false;

    //文件名称字段排序,只有导出zip时才会用到
    int fileNameFiledSort() default 0;
}
//测试数据对象
@Data
public class Something{
    @ExportWordField(fieldName = "user",isFileNameFiled = true,fileNameFiledSort = 1)
    private String user;
    @ExportWordField(fieldName = "animals")
    private List<Animal> animals;
}

@Data
public class Animal{
    @ExportWordField(fieldName = "name")
    private String name;
    @ExportWordField(fieldName = "price")
    private String price;
}
  • 3.然后在代码中将数据写入到模板中,具体代码如下:
@Slf4j
public class ExportTemplateUtil {

    private static final String DOC_SUFFIX = ".doc";

    /**
     * @Description: 导出word文件放在zip里面
     * @param zipName 压缩包名称
     * @param templateName 模板名称,默认取resources下的template文件夹
     * @param dataList 数据集合
     * @param clazz 数据类型
     * @param <T> 泛型
     */
    public static <T>void exportListFileOnZip(HttpServletResponse response, String zipName, String templateName, List<T> dataList,Class<T> clazz){
        //所有数据的输出流
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        // 压缩流
        ZipOutputStream zipOutputStream = null;
        try {
            response.setContentType("application/octet-stream;charset=UTF-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(zipName, "UTF-8") + ".zip");
            zipOutputStream = new ZipOutputStream(response.getOutputStream());
            Template template = getTemplate(templateName,clazz);
            Map<String,Integer> nameMap = new HashMap<>();
            for (T data : dataList) {
                String fileName = getFileName(data);
                if (nameMap.containsKey(fileName)) {
                    nameMap.put(fileName,nameMap.get(fileName) +1);
                    fileName = fileName+"(" + nameMap.get(fileName) +")";
                }else {
                    nameMap.put(fileName,0);
                }
                ByteArrayOutputStream byteArrayOutputStream = generateOneFile(template,data);
                outputStream.write(byteArrayOutputStream.toByteArray());
                ZipEntry zipEntry = new ZipEntry(fileName+DOC_SUFFIX);
                zipOutputStream.putNextEntry(zipEntry);
                zipOutputStream.write(byteArrayOutputStream.toByteArray());
                zipOutputStream.closeEntry();
            }
            zipOutputStream.write(outputStream.toByteArray());
            zipOutputStream.closeEntry();
            zipOutputStream.flush();
            zipOutputStream.close();
        } catch (Exception e) {
            log.error("导出word文件放在zip里面异常",e);
        }finally {
            try {
                outputStream.close();
                zipOutputStream.close();
            } catch (IOException e) {
                log.error("导出word文件放在zip里面异常",e);
            }
        }
    }


    /**
     * @Description: 导出word文件所有数据都放在一个文件里面
     * @param fileName 文件名称
     * @param templateName 模板名称,默认取resources下的template文件夹
     * @param dataList 数据集合
     * @param clazz 数据类型
     * @param <T> 泛型
     */
    public static <T> void exportListOnOneFile(HttpServletResponse response,String fileName,String templateName, List<T> dataList,Class<T> clazz){
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try {
            response.setContentType("application/msword;charset=UTF-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".doc");
            Template template = getTemplate(templateName,clazz);
            for (T data : dataList) {
                ByteArrayOutputStream byteArrayOutputStream = generateOneFile(template,data);
                outputStream.write(byteArrayOutputStream.toByteArray());
            }
            response.getOutputStream().write(outputStream.toByteArray());
            response.getOutputStream().flush();
            response.getOutputStream().close();
        } catch (Exception e) {
            log.error("导出word文件失败",e);
        }finally {
            try {
                outputStream.close();
            } catch (IOException e) {
                log.error("关闭流失败",e);
            }
        }
    }

    /**
     * 导出提条数据放在一个word文件里面
     * @param response 响应
     * @param fileName 文件名称
     * @param templateName 模板名称,默认取resources下的template文件夹
     * @param data 数据
     * @param clazz 数据类型
     * @param <T> 泛型
     * @throws Exception 异常
     */
    public static <T> void exportOneFile(HttpServletResponse response, String fileName, String templateName, T data, Class<T> clazz) throws Exception{
        Template template = getTemplate(templateName,clazz);
        ByteArrayOutputStream outputStream = generateOneFile(template,data);
        response.setContentType("application/msword;charset=UTF-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes("UTF-8"), "ISO8859-1") + ".doc");
        response.getOutputStream().write(outputStream.toByteArray());
        response.getOutputStream().flush();
        response.getOutputStream().close();
    }


    /**
     * 生成一个word文件的字节流
     * @param data 数据
     * @param <T> 泛型
     * @return 字节流
     * @throws Exception 异常
     */
    private static <T> ByteArrayOutputStream generateOneFile(Template template,T data) throws Exception{
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        OutputStreamWriter writer = new OutputStreamWriter(outputStream);
        HashMap<String, Object> dataMap =  convertDataToHashMap(data);
        if (null == dataMap || dataMap.size() == 0) {
            log.error("数据为空");
            throw new RuntimeException("数据为空");
        }
        template.process(dataMap, writer);
        return outputStream;
    }

    /**
     * 转换数据为hashMap供freemarker使用
     * @param data 数据
     * @param <T> 泛型
     * @return hashMap
     */
    private static <T>HashMap<String,Object> convertDataToHashMap(T data){
        //使用反射获取类的属性
        Class<?> myClass = data.getClass();
        Field[] fields = myClass.getDeclaredFields();
        HashMap<String,Object> dataMap = new HashMap<>();
        for (Field field : fields) {
            if (field.isAnnotationPresent(ExportWordField.class)) {
                //允许访问私有变量
                field.setAccessible(true);
                //如果是list类型的数据需要深度遍历处理
                if (field.getType().equals(List.class)) {
                    List<?> list = null;
                    try {
                        list = convertListDataToHashMap((List<?>) field.get(data));
                    } catch (IllegalAccessException e) {
                        log.error("获取属性值失败",e);
                        throw new RuntimeException("获取属性值失败");
                    }
                    dataMap.put(field.getName(),list);
                    continue;
                }
                ExportWordField exportWordField = field.getAnnotation(ExportWordField.class);
                String fieldName = exportWordField.fieldName();
                try {
                    if (fieldName != null && !"".equals(fieldName)) {
                        dataMap.put(fieldName,  field.get(data) == null ? "" : field.get(data));
                    }else {
                        dataMap.put(field.getName(), field.get(data) == null ? "" : field.get(data));
                    }
                } catch (IllegalAccessException e) {
                    log.error("获取属性值失败",e);
                    throw new RuntimeException("获取属性值失败");
                }
            }
        }
        return dataMap;
    }

    /**
     * 如果fidel是list类型的数据则需要判断此层是否有list类型的如果有需要不停的往底层变量一直到最后一层然后取每一层符合ExportWordField的数据返回给上一层
     * @param list list数据
     * @param <T> 泛型
     * @return hashMap
     */
    private static <T> List<HashMap<String, Object>> convertListDataToHashMap(List<T> list) {
        List<HashMap<String, Object>> listMap = new ArrayList<>();
        if (!CollectionUtils.isEmpty(list)) {
            for (T t : list) {
                HashMap<String, Object> hashMap = convertDataToHashMap(t);
                listMap.add(hashMap);
            }
        }
        return listMap;
    }

    /**
     * @Description: 读取模板文件
     * @param templateName 模板名称,默认取resources下的template文件夹
     * @return freemarker.template.Template
     */
    private static <T>Template getTemplate(String templateName,Class<T> clazz) throws IOException {
        Configuration configuration = new Configuration(Configuration.getVersion());
        configuration.setDefaultEncoding("UTF-8");
        configuration.setClassForTemplateLoading(clazz, "/templates");
        return configuration.getTemplate(templateName);
    }
    
    private static <T> String getFileName(T data){
        Field[] fields = data.getClass().getDeclaredFields();
        StringBuilder fileName = new StringBuilder();
        Arrays.stream(fields).filter(field -> field.getAnnotation(ExportWordField.class) != null
                && field.getAnnotation(ExportWordField.class).isFileNameFiled()).sorted((o1, o2) -> {
            ExportWordField annotation1 = o1.getAnnotation(ExportWordField.class);
            ExportWordField annotation2 = o2.getAnnotation(ExportWordField.class);
            return annotation1.fileNameFiledSort() - annotation2.fileNameFiledSort();
        }).forEach(field -> {
            field.setAccessible(true);
            try {
                fileName.append(field.get(data)+"-");
            } catch (IllegalAccessException e) {
                log.error("获取文件名称异常",e);
            }
        });
        return fileName.toString();
    }
}

3.使用

  //导出压缩包
  ExportTemplateUtil.exportListFileOnZip(response,"测试导出","template2.0.ftl",somethingObj,Something.class);
  //导出一个文件
  ExportTemplateUtil.exportOneFile(response,"测试导出","template2.0.ftl",somethingObj,Something.class);

文章来源地址https://www.toymoban.com/news/detail-749631.html

到了这里,关于使用freemarker,导出制作好的ftl模板,并写入数据的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • aspose 使用ftl模板生成word和pdf

    1 先找到word模板,用${},替换变量,保存,然后另存为xml,最后把xml后缀改成ftl。 如下图: word 模板文件 ftl模板文件如下: 2 代码生成 下面函数将ftl填充数据,并生成word和pdf 3 测试主程序 4 结果: pdf文件 word文件 还可以生成图片:

    2024年02月13日
    浏览(40)
  • ubuntu深度学习使用TensorFlow卷积神经网络——图片数据集的制作以及制作好的数据集的使用

    首先我事先准备好五分类的图片放在对应的文件夹,图片资源在我的gitee文件夹中链接如下: 文件管理: 用于存各种数据 https://gitee.com/xiaoxiaotai/file-management.git  里面有imgs目录和npy目录,imgs就是存放5分类的图片的目录,里面有桂花、枫叶、五味子、银杏、竹叶5种植物,npy目

    2024年02月05日
    浏览(71)
  • freemarker 使用word模板赋值

    这里贴上自己测试的ftl文件 生成的效果 参考: https://blog.csdn.net/weixin_46174854/article/details/116855252 https://blog.csdn.net/weixin_45853881/article/details/129298494 https://blog.csdn.net/qq_42851623/article/details/122879852 https://blog.csdn.net/weixin_45103378/article/details/118395284 https://www.cnblogs.com/ayueC/p/15118381.html

    2024年02月11日
    浏览(45)
  • 若依框架中使用FreeMarker使用word动态模板生成pdf给前端展示(模板中并没用使用到图片,所以没有图片的方法,只用了表格展示数据,模板里面只涉及到了循环判断和日期格式)

    首先使用word创建动态模板  下方两组信息是通过循环展示的,在生成模板时注意,如果不点击里面表格,选择居中表格打印出来可能还有偏差,两边距离页面拒了可能不一样 存储需要的模板时注意      存成这个格式,如果不是2003可能会有坑,找到你生成的.xml文件,把后缀改成ftl  

    2024年02月04日
    浏览(45)
  • freemarker模板引擎详解以及使用方法

    哈喽!大家好,我是旷世奇才李先生 文章持续更新,可以微信搜索【小奇JAVA面试】第一时间阅读,回复【资料】更有我为大家准备的福利哟,回复【项目】获取我为大家准备的项目 相关阅读 面试官:Zookeeper是什么,它有什么特性与使用场景? 面试官:Redis如何实现持久化的

    2024年02月09日
    浏览(43)
  • Java使用ftl模板文件生成Word,以及Word转换图片或Pdf工具类

    一、写在前面 最近在项目中使用打印功能,发现这个功能我已经写过多次了,下面这个文章的发步日期在2020年,不得不感慨时间之快啊。 https://blog.csdn.net/weixin_43238452/article/details/109636200?spm=1001.2014.3001.5501 下面介绍一下应用场景:这次项目依旧是springboot项目,使用ftl模版生

    2024年02月15日
    浏览(59)
  • 根据模板动态生成word(一)使用freemarker生成word

    @ 目录 一、准备模板 1、创建模板文件 2、处理模板 2.1 处理普通文本 2.2 处理表格 2.3 处理图片 二、项目代码 1、引入依赖 2、生成代码 三、验证生成word 首先先建立一个word文件,输入模板内容freemaker的内容,下面是本次演示的word文件。 然后将word文件另存为 .xml 文件,然后再

    2024年02月13日
    浏览(85)
  • freemarker模板在客服域的使用场景及用法介绍

    🍊 Java学习:社区快速通道 🍊 深入浅出RocketMQ设计思想:深入浅出RocketMQ设计思想 🍊 绝对不一样的职场干货:大厂最佳实践经验指南 📆 最近更新:2023年7月15日 🍊 点赞 👍 收藏 ⭐留言 📝 都是我最大的动力! freemarker.template 是一个Java库,用于生成文本输出(如HTML、X

    2024年02月16日
    浏览(36)
  • 使用easyexcel填充模板数据,并导出excel

    导出excel功能非常场景,本片文章记录如何使用模板填充数据后再导出。因直接导出excel数据样式不符合要求,所以做了模板填充然后再导出excel。 效果如下: 注意:列表数据变量名前面要写点{.id},如果单条数据可以不写。 使用表单提交: 实体代码: controller代码: 只对je

    2024年03月11日
    浏览(53)
  • Java根据模版导出(ftl方式)

    实际项目中经常遇到需要根据模版导出数据,普通一点的导出模版都挺好实现的,如果涉及到勾选框、表格循环的方式就比较麻烦一点,这篇文章主要记录一下我在项目中是如何导出word(其中包括根据值勾选、表格循环、图片) 一、先准备一份word模版 如图: 此模版主要是

    2024年02月07日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包