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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM