• 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
  • 相关阅读:
    为图片指定区域添加链接
    数值取值范围问题
    【leetcode】柱状图中最大的矩形(第二遍)
    【leetcode 33】搜索旋转排序数组(第二遍)
    【Educational Codeforces Round 81 (Rated for Div. 2) C】Obtain The String
    【Educational Codeforces Round 81 (Rated for Div. 2) B】Infinite Prefixes
    【Educational Codeforces Round 81 (Rated for Div. 2) A】Display The Number
    【Codeforces 716B】Complete the Word
    一个简陋的留言板
    HTML,CSS,JavaScript,AJAX,JSP,Servlet,JDBC,Structs,Spring,Hibernate,Xml等概念
  • 原文地址:https://www.cnblogs.com/10158wsj/p/11211471.html
Copyright © 2020-2023  润新知