ToB企服应用市场:ToB评测及商务社交产业平台

标题: 使用freemarker,导出制作好的ftl模板,并写入数据 [打印本页]

作者: 刘俊凯    时间: 2024-2-9 11:47
标题: 使用freemarker,导出制作好的ftl模板,并写入数据
使用freemarker,导出制作好的ftl模板,并写入数据

一、背景

1.1 项目背景

  1. <dependency>
  2.     <groupId>org.freemarker</groupId>
  3.     <artifactId>freemarker</artifactId>
  4.     <version>2.3.28</version>
  5. </dependency>
复制代码
二、实现

2.1 代码实现

  1. <html>
  2. <head>
  3.   <title>Welcome!</title>
  4. </head>
  5. <body>
  6.   <h1>Welcome ${user}!</h1>
  7.   <p>We have there animals:
  8.   <ul>
  9.   <list animals as animal>
  10.     <li>${animal.name} for ${animal.price} Euros
  11.   </list>
  12.   </ul>
  13.   <include "common_footer.html">
  14. </body>
  15. </html>
复制代码
  1. @Documented
  2. @Target({ElementType.FIELD})
  3. @Retention(RetentionPolicy.RUNTIME)
  4. @Inherited
  5. public @interface ExportWordField {
  6.     //字段名称
  7.     String fieldName() default "";
  8.     //是否是文件名称字段,只有导出zip时才会用到
  9.     boolean isFileNameFiled() default false;
  10.     //文件名称字段排序,只有导出zip时才会用到
  11.     int fileNameFiledSort() default 0;
  12. }
复制代码
  1. //测试数据对象
  2. @Data
  3. public class Something{
  4.     @ExportWordField(fieldName = "user",isFileNameFiled = true,fileNameFiledSort = 1)
  5.     private String user;
  6.     @ExportWordField(fieldName = "animals")
  7.     private List<Animal> animals;
  8. }
  9. @Data
  10. public class Animal{
  11.     @ExportWordField(fieldName = "name")
  12.     private String name;
  13.     @ExportWordField(fieldName = "price")
  14.     private String price;
  15. }
复制代码
  1. @Slf4j
  2. public class ExportTemplateUtil {
  3.     private static final String DOC_SUFFIX = ".doc";
  4.     /**
  5.      * @Description: 导出word文件放在zip里面
  6.      * @param zipName 压缩包名称
  7.      * @param templateName 模板名称,默认取resources下的template文件夹
  8.      * @param dataList 数据集合
  9.      * @param clazz 数据类型
  10.      * @param <T> 泛型
  11.      */
  12.     public static <T>void exportListFileOnZip(HttpServletResponse response, String zipName, String templateName, List<T> dataList,Class<T> clazz){
  13.         //所有数据的输出流
  14.         ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
  15.         // 压缩流
  16.         ZipOutputStream zipOutputStream = null;
  17.         try {
  18.             response.setContentType("application/octet-stream;charset=UTF-8");
  19.             response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(zipName, "UTF-8") + ".zip");
  20.             zipOutputStream = new ZipOutputStream(response.getOutputStream());
  21.             Template template = getTemplate(templateName,clazz);
  22.             Map<String,Integer> nameMap = new HashMap<>();
  23.             for (T data : dataList) {
  24.                 String fileName = getFileName(data);
  25.                 if (nameMap.containsKey(fileName)) {
  26.                     nameMap.put(fileName,nameMap.get(fileName) +1);
  27.                     fileName = fileName+"(" + nameMap.get(fileName) +")";
  28.                 }else {
  29.                     nameMap.put(fileName,0);
  30.                 }
  31.                 ByteArrayOutputStream byteArrayOutputStream = generateOneFile(template,data);
  32.                 outputStream.write(byteArrayOutputStream.toByteArray());
  33.                 ZipEntry zipEntry = new ZipEntry(fileName+DOC_SUFFIX);
  34.                 zipOutputStream.putNextEntry(zipEntry);
  35.                 zipOutputStream.write(byteArrayOutputStream.toByteArray());
  36.                 zipOutputStream.closeEntry();
  37.             }
  38.             zipOutputStream.write(outputStream.toByteArray());
  39.             zipOutputStream.closeEntry();
  40.             zipOutputStream.flush();
  41.             zipOutputStream.close();
  42.         } catch (Exception e) {
  43.             log.error("导出word文件放在zip里面异常",e);
  44.         }finally {
  45.             try {
  46.                 outputStream.close();
  47.                 zipOutputStream.close();
  48.             } catch (IOException e) {
  49.                 log.error("导出word文件放在zip里面异常",e);
  50.             }
  51.         }
  52.     }
  53.     /**
  54.      * @Description: 导出word文件所有数据都放在一个文件里面
  55.      * @param fileName 文件名称
  56.      * @param templateName 模板名称,默认取resources下的template文件夹
  57.      * @param dataList 数据集合
  58.      * @param clazz 数据类型
  59.      * @param <T> 泛型
  60.      */
  61.     public static <T> void exportListOnOneFile(HttpServletResponse response,String fileName,String templateName, List<T> dataList,Class<T> clazz){
  62.         ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
  63.         try {
  64.             response.setContentType("application/msword;charset=UTF-8");
  65.             response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".doc");
  66.             Template template = getTemplate(templateName,clazz);
  67.             for (T data : dataList) {
  68.                 ByteArrayOutputStream byteArrayOutputStream = generateOneFile(template,data);
  69.                 outputStream.write(byteArrayOutputStream.toByteArray());
  70.             }
  71.             response.getOutputStream().write(outputStream.toByteArray());
  72.             response.getOutputStream().flush();
  73.             response.getOutputStream().close();
  74.         } catch (Exception e) {
  75.             log.error("导出word文件失败",e);
  76.         }finally {
  77.             try {
  78.                 outputStream.close();
  79.             } catch (IOException e) {
  80.                 log.error("关闭流失败",e);
  81.             }
  82.         }
  83.     }
  84.     /**
  85.      * 导出提条数据放在一个word文件里面
  86.      * @param response 响应
  87.      * @param fileName 文件名称
  88.      * @param templateName 模板名称,默认取resources下的template文件夹
  89.      * @param data 数据
  90.      * @param clazz 数据类型
  91.      * @param <T> 泛型
  92.      * @throws Exception 异常
  93.      */
  94.     public static <T> void exportOneFile(HttpServletResponse response, String fileName, String templateName, T data, Class<T> clazz) throws Exception{
  95.         Template template = getTemplate(templateName,clazz);
  96.         ByteArrayOutputStream outputStream = generateOneFile(template,data);
  97.         response.setContentType("application/msword;charset=UTF-8");
  98.         response.setHeader("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes("UTF-8"), "ISO8859-1") + ".doc");
  99.         response.getOutputStream().write(outputStream.toByteArray());
  100.         response.getOutputStream().flush();
  101.         response.getOutputStream().close();
  102.     }
  103.     /**
  104.      * 生成一个word文件的字节流
  105.      * @param data 数据
  106.      * @param <T> 泛型
  107.      * @return 字节流
  108.      * @throws Exception 异常
  109.      */
  110.     private static <T> ByteArrayOutputStream generateOneFile(Template template,T data) throws Exception{
  111.         ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
  112.         OutputStreamWriter writer = new OutputStreamWriter(outputStream);
  113.         HashMap<String, Object> dataMap =  convertDataToHashMap(data);
  114.         if (null == dataMap || dataMap.size() == 0) {
  115.             log.error("数据为空");
  116.             throw new RuntimeException("数据为空");
  117.         }
  118.         template.process(dataMap, writer);
  119.         return outputStream;
  120.     }
  121.     /**
  122.      * 转换数据为hashMap供freemarker使用
  123.      * @param data 数据
  124.      * @param <T> 泛型
  125.      * @return hashMap
  126.      */
  127.     private static <T>HashMap<String,Object> convertDataToHashMap(T data){
  128.         //使用反射获取类的属性
  129.         Class<?> myClass = data.getClass();
  130.         Field[] fields = myClass.getDeclaredFields();
  131.         HashMap<String,Object> dataMap = new HashMap<>();
  132.         for (Field field : fields) {
  133.             if (field.isAnnotationPresent(ExportWordField.class)) {
  134.                 //允许访问私有变量
  135.                 field.setAccessible(true);
  136.                 //如果是list类型的数据需要深度遍历处理
  137.                 if (field.getType().equals(List.class)) {
  138.                     List<?> list = null;
  139.                     try {
  140.                         list = convertListDataToHashMap((List<?>) field.get(data));
  141.                     } catch (IllegalAccessException e) {
  142.                         log.error("获取属性值失败",e);
  143.                         throw new RuntimeException("获取属性值失败");
  144.                     }
  145.                     dataMap.put(field.getName(),list);
  146.                     continue;
  147.                 }
  148.                 ExportWordField exportWordField = field.getAnnotation(ExportWordField.class);
  149.                 String fieldName = exportWordField.fieldName();
  150.                 try {
  151.                     if (fieldName != null && !"".equals(fieldName)) {
  152.                         dataMap.put(fieldName,  field.get(data) == null ? "" : field.get(data));
  153.                     }else {
  154.                         dataMap.put(field.getName(), field.get(data) == null ? "" : field.get(data));
  155.                     }
  156.                 } catch (IllegalAccessException e) {
  157.                     log.error("获取属性值失败",e);
  158.                     throw new RuntimeException("获取属性值失败");
  159.                 }
  160.             }
  161.         }
  162.         return dataMap;
  163.     }
  164.     /**
  165.      * 如果fidel是list类型的数据则需要判断此层是否有list类型的如果有需要不停的往底层变量一直到最后一层然后取每一层符合ExportWordField的数据返回给上一层
  166.      * @param list list数据
  167.      * @param <T> 泛型
  168.      * @return hashMap
  169.      */
  170.     private static <T> List<HashMap<String, Object>> convertListDataToHashMap(List<T> list) {
  171.         List<HashMap<String, Object>> listMap = new ArrayList<>();
  172.         if (!CollectionUtils.isEmpty(list)) {
  173.             for (T t : list) {
  174.                 HashMap<String, Object> hashMap = convertDataToHashMap(t);
  175.                 listMap.add(hashMap);
  176.             }
  177.         }
  178.         return listMap;
  179.     }
  180.     /**
  181.      * @Description: 读取模板文件
  182.      * @param templateName 模板名称,默认取resources下的template文件夹
  183.      * @return freemarker.template.Template
  184.      */
  185.     private static <T>Template getTemplate(String templateName,Class<T> clazz) throws IOException {
  186.         Configuration configuration = new Configuration(Configuration.getVersion());
  187.         configuration.setDefaultEncoding("UTF-8");
  188.         configuration.setClassForTemplateLoading(clazz, "/templates");
  189.         return configuration.getTemplate(templateName);
  190.     }
  191.    
  192.     private static <T> String getFileName(T data){
  193.         Field[] fields = data.getClass().getDeclaredFields();
  194.         StringBuilder fileName = new StringBuilder();
  195.         Arrays.stream(fields).filter(field -> field.getAnnotation(ExportWordField.class) != null
  196.                 && field.getAnnotation(ExportWordField.class).isFileNameFiled()).sorted((o1, o2) -> {
  197.             ExportWordField annotation1 = o1.getAnnotation(ExportWordField.class);
  198.             ExportWordField annotation2 = o2.getAnnotation(ExportWordField.class);
  199.             return annotation1.fileNameFiledSort() - annotation2.fileNameFiledSort();
  200.         }).forEach(field -> {
  201.             field.setAccessible(true);
  202.             try {
  203.                 fileName.append(field.get(data)+"-");
  204.             } catch (IllegalAccessException e) {
  205.                 log.error("获取文件名称异常",e);
  206.             }
  207.         });
  208.         return fileName.toString();
  209.     }
  210. }
复制代码
3.使用
  1.   //导出压缩包
  2.   ExportTemplateUtil.exportListFileOnZip(response,"测试导出","template2.0.ftl",somethingObj,Something.class);
  3.   //导出一个文件
  4.   ExportTemplateUtil.exportOneFile(response,"测试导出","template2.0.ftl",somethingObj,Something.class);
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4