• freemarker实现单元格动态合并-行合并


    项目需求:项目中有个需求,需要将一些数据库中的数据根据需求导出,生成一个word,研究了一些技术,其中包括POI、freemaker,对比了一下实现过程及技术难度没最终使用了freemaker;

    原始文件

    效果:

    实现过程大概分为三步,第一步:根据word文件做模板,修改模板,导出word。这里主要记录一下过程中遇到的一些情况。

    一、制作模板

     打开word文件,为要添加数据的地方打标签,,使用${key}的方式,key将来就是数据Map中的key,需要保持一致。然后另存为XML,因为word本质上就是个XML

     

    二、处理XML文件

     使用FirstObject XML Edito或者其他工具打开,我是用的是NotePad++,打开需要格式话一下,要不然没法看,打开主要是处理以下的情况:

     这个是分开的,需要将中间的部分删除,处理后如下:

    全部处理好之后,文件另存为ftl文件,这个就是我们制作完成的模板。

    三、数据处理、打标签

     这里主要说的是需要循环并且还需要合并单元格的情况。如果没有这些情况,直接打标签,封装数据就可以了。

    第一个:list

    主要是将数据封装到list中,list中是若干个Map,如图:

    注意:list一定要放在要循环行开始的位置,及前一行结束的位置:

    list结束位置:

    四、数据准备

     数据封装,数据放在Map中,这里Map中的key就是${key}中的key:

     public void exportWord(HttpServletRequest request, HttpServletResponse response){
            Map<String, Object> dataMap = new HashMap<>(16);
            dataMap.put("total", "10");
            List<Map<String,String>> list=new ArrayList<>( 16 );
            for(int i=0;i<3;i++){
                Map<String, String>  listMap= new HashMap<>(16);
                listMap.put( "no","10000"+i );
                listMap.put( "name","test"+i );
                listMap.put( "introduce","介绍"+i );
                list.add( listMap );
            }
            dataMap.put( "whyc",checkList( list ) );
    
            System.out.println(dataMap);
            WordUtil.exportMillCertificateWord(response,dataMap,"test.docx","test.ftl");
        }
    
    
        public List<Map<String, String>> checkList(List<Map<String, String>> list) {
            String start = "<w:vMerge w:val='restart'/>";
            String end = "<w:vMerge/>";
            list.get(0).put("start", start);
            for (int i = 1; i < list.size(); i++) {
                    list.get(i).put("end", end);
            }
            return list;
        }
    View Code
    下面重点来了,合并单元格,如何合并单元格,其实很简单,用到的就是:"<w:vMerge w:val='restart'/>"和"<w:vMerge/>",这两个命令放的位置很重要,否则不会合并成功!

    原则一、第一行数据只放"<w:vMerge w:val='restart'/>",从第二行开始,所有要合并的单元格放"<w:vMerge/>"。比如,我这个total这一行要合并,所以要这么处理:

    public List<Map<String, String>> checkList(List<Map<String, String>> list) {
            String start = "<w:vMerge w:val='restart'/>";
            String end = "<w:vMerge/>";
            list.get(0).put("start", start);
            for (int i = 1; i < list.size(); i++) {
                    list.get(i).put("end", end);
            }
            return list;
        }
    View Code

    数据输出是这样的:

    原则二、编辑ftl模板文件,start和end的位置,一定放在行循环开始的<w:tcPr>中:

    最后是文件导出及下载的代码:

    package com.thupdi.project.utils;
    
    import freemarker.template.Configuration;
    import freemarker.template.Template;
    
    import javax.servlet.ServletOutputStream;
    import javax.servlet.http.HttpServletResponse;
    import java.io.*;
    import java.net.URLEncoder;
    import java.util.Map;
    
    /**
     * @Author wangshuaijun
     * @Date 2019/7/18 13:59
     * @Version 1.0
     */
    public class WordUtil {
        private static Configuration configure;
    
    
        static {
            configure= new Configuration();
            configure.setDefaultEncoding("utf-8");
    //        configure.setClassForTemplateLoading(WordUtil.class,"com/thupdi/project/templates");
            try {
                configure.setDirectoryForTemplateLoading(new File( "D:/develop/IJWorkspaces/fzlsmc/src/main/resources/wordteplate" ));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        public WordUtil() {
            super();
        }
    
        /**
         * 根据Doc模板生成word文件
         * @param fileName 文件名称
         * @throws IOException
         */
        public static void exportMillCertificateWord(HttpServletResponse response,
                                                     Map<String,Object> map, String fileName, String ftlFile){
    
            Template freemarkerTemplate = null;
            try {
                freemarkerTemplate = configure.getTemplate(ftlFile);
            } catch (IOException e1) {
                e1.printStackTrace();
            }
    
            File file = null;
            InputStream fin = null;
            ServletOutputStream out = null;
            try {
                // 调用工具类的createDoc方法生成Word文档
                file = createDoc(map,freemarkerTemplate,fileName);
                fin = new FileInputStream(file);
    
                response.setCharacterEncoding("utf-8");
                response.setContentType("application/msword");
                // 设置浏览器以下载的方式处理该文件名
                fileName = fileName + ".doc";
                response.setHeader("Content-Disposition", "attachment;filename="
                        .concat(String.valueOf( URLEncoder.encode(fileName, "UTF-8"))));
    
                out = response.getOutputStream();
                byte[] buffer = new byte[512];  // 缓冲区
                int bytesToRead = -1;
                // 通过循环将读入的Word文件的内容输出到浏览器中
                while((bytesToRead = fin.read(buffer)) != -1) {
                    out.write(buffer, 0, bytesToRead);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (fin != null) {
                    try {
                        fin.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (out != null) {
                    try {
                        out.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                // 删除临时文件
                if (file != null) {
                    file.delete();
                }
            }
        }
        /**
         * 生成word
         * @param dataMap
         * @param template
         * @param fileName
         * @return
         */
        private static File createDoc(Map<?, ?> dataMap, Template template,String fileName) {
            File f = new File(fileName);
            Template t = template;
            try {
                // 这个地方不能使用FileWriter因为需要指定编码类型否则生成的Word文档会因为有无法识别的编码而无法打开
                Writer w = new OutputStreamWriter(new FileOutputStream(f), "utf-8");
                t.process(dataMap, w);
                w.close();
            } catch (Exception ex) {
                ex.printStackTrace();
                throw new RuntimeException(ex);
            }
            return f;
        }
    }
    View Code

    Maven依赖:

     <!-- https://mvnrepository.com/artifact/org.freemarker/freemarker -->
            <dependency>
                <groupId>org.freemarker</groupId>
                <artifactId>freemarker</artifactId>
                <version>2.3.20</version>
            </dependency>
    View Code
  • 相关阅读:
    体验一下:AndroidX
    Android研发技术的进阶之路
    App 冷启动与热启动及启动白屏优化
    Android Q 正式命名为 Android 10
    Android开发学习路线的七个阶段和步骤
    安卓旅途之——开发数独(一)
    项目总结
    小组互评与自评
    典型用户与场景
    第二个Sprint计划
  • 原文地址:https://www.cnblogs.com/10158wsj/p/11211471.html
Copyright © 2020-2023  润新知