freemarker個性化導出excel

前言

導出excel是比較常見的需求,有時候需要導出的excel有固定的格式,這裏記錄一下FreeMarker導出excel,暫時不瞭解FreeMarker基本語法的請先去學習一下。

正文

FreeMarker是一款模板引擎: 即一種基於模板和要改變的數據, 並用來生成輸出文本(HTML網頁、電子郵件、配置文件、源代碼等)的通用工具。 它不是面向最終用戶的,而是一個Java類庫,是一款程序員可以嵌入他們所開發產品的組件。(百度百科)

FreeMarker導出的步驟大概就三步:製作模板–>取數據–>渲染導出excel

它能導出的不限於excel,上面說了FreeMarker是一個模板引擎,能夠生成各類文件,大概如下:
在這裏插入圖片描述

圖片來自網絡

製作模板

製作模板,以excel爲例,先將有格式的excel另存爲xml格式的文件
在這裏插入圖片描述

excel樣品(假裝有格式)

實際中需求的格式可能比較複雜,但是沒有關係,因爲製作模板不會對其樣式進行操作,只涉及修改數據填充的部分,所以樣式不管再複雜,都是一樣的。

將excel另存爲xml,這裏我選擇的是 “XML 電子表格2003” ,因爲有些電腦上的安裝的wps或Excel版本比較低,所以儘量選擇兼容性高的xml格式。

在這裏插入圖片描述

導出的xml文件仔細看,就能將其和原文件聯繫起來,對應爲sheet1的3行3列數據。
在這裏插入圖片描述

最後我需要導出的文件是這樣的:
在這裏插入圖片描述

修改xml文件如下:
在這裏插入圖片描述

設置了rowcount(表格的行數),修改了表格內容標籤,遍歷list進行渲染。

最後修改文件後綴爲.ftl,製作模板大功告成。

取數據

取數據這一步就根據模板定義的變量進行渲染就好了,這一步就不多介紹了,我模擬了一些數據

    private Map<String, Object> getDataMap() {
        Map map = new HashMap(16);
        //list demo
        List<Map<String,String>> dataList = new LinkedList();
        for (int i = 0; i<10; i++){
            Map eleMap = new HashMap(16);
            eleMap.put("name","姓名"+i+1);
            eleMap.put("education","本科");
            eleMap.put("age",i+20);
            dataList.add(eleMap);
        }
        map.put("datalist", dataList);
//        表格總共有多少行
        map.put("rowcount", dataList.size() + 1 +1);
        return map;
    }

渲染導出excel

導出操作,就是調用Freemarker解析模板,並渲染數據,最後將文件導出。

ExcelUtils:

import freemarker.template.Configuration;
import freemarker.template.Template;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author
 */
public class ExcelUtils {
    private static Configuration configuration = null;
    private static Map<String, Template> allTemplates = null;
//    private static String realPath = ClassPathUtil.getDeployWarPath();

    public ExcelUtils() {
    }

    /**
     * 創建excel
     *
     * @param dataMap
     * @param type
     * @return
     */
    public File createExcel(Map<?, ?> dataMap, String type, String valueName) {
        try {
            configuration = new Configuration();
            configuration.setDefaultEncoding(type);
            //配置路徑,有兩種方式setDirectoryForTemplateLoading和setClassForTemplateLoading
//            configuration.setDirectoryForTemplateLoading(new File(realPath+"ftltemplate"));
            configuration.setClassForTemplateLoading(this.getClass(), "/ftl");
            allTemplates = new HashMap<String, Template>();
            allTemplates.put(type, configuration.getTemplate(valueName));
        } catch (IOException ex) {
            ex.printStackTrace();
            throw new RuntimeException(ex);
        }
        String name = "temp" + (int) (Math.random() * 100000) + ".xls";
        File file = new File(name);
        //取模板
        Template template = allTemplates.get(type);
        try {
            Writer w = new OutputStreamWriter(new FileOutputStream(file), type);
            //將map的數據生成到文件
            template.process(dataMap, w);
            w.close();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        return file;
    }

    /**
     * 導出文件,將文件存到瀏覽器設置的下載位置
     */
    public void download(File file, String fileName) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        String time = sdf.format(new Date());
        String filename = fileName + time + ".xls";//即將下載的文件名字
        InputStream ins = null;
        try {
            ins = new FileInputStream(file);
            filename = URLEncoder.encode(filename, "UTF-8");
            HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
            OutputStream os = new BufferedOutputStream(response.getOutputStream());
            response.reset();
            response.setContentType("application/x-download");
            response.addHeader("Content-Disposition", "attachment;filename=" + filename);
            response.setContentType("application/vnd.ms-excel");
            byte[] buff = new byte[2048];
            int bytesRead;
            while (-1 != (bytesRead = ins.read(buff, 0, buff.length))) {
                os.write(buff, 0, bytesRead);
            }
            ins.close();
            os.close();
            String str = "導出" + fileName + "成功!";
            System.out.println(str);
        } catch (Exception e) {
            e.printStackTrace();
            String str1 = "導出" + fileName + "失敗!";
            System.out.println(str1);
        }
    }

}

導出:

    @RequestMapping("/download")
    public void download(){
        File file = null;
        ExcelUtils excelutil = new ExcelUtils();
        //獲取數據
        Map<String, Object> map = getDataMap();
        //導出
        try {
            file = excelutil.createExcel(map, "UTF-8", "testfreemarker.ftl");
            excelutil.download(file, "導出文件");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (file != null)
                file.delete(); // 刪除臨時文件
        }
    }

導出總結

FreeMarker導出的主要工作就是在模板的製作和數據的獲取上面,整體代碼結構比較簡單,我這裏使用的springboot構建的一個Maven項目,換成非springboot的也一樣,就幾個文件,拷貝到項目裏就能跑了,工程結構如下:
在這裏插入圖片描述

代碼我已上傳到CSDN點擊下載,部署後直接訪問http://localhost:8081/download即可。

常見問題

導出的文件打不開報錯

一般報錯如下:
在這裏插入圖片描述

根據路徑,打開這個日誌文件(文件夾可能是隱藏的,直接在路徑上輸入\AppData\Local\Microsoft\Windows\INetCache\Content.MSO)。

1.行數值太小

在這裏插入圖片描述

這裏很明確的指示了 ExpandedRowCount 值爲 2 無效,ExpandedRowCount 在xml裏面表示行數,當你的數據渲染超過2行時,Excel對這些要求比較嚴格,導致文件打不開。

**解決辦法:**將ExpandedRowCount 的大小傳過來,或者設置的足夠大。
在這裏插入圖片描述

2.參數類型不匹配

在這裏插入圖片描述

數據類型不匹配,Number類型,不允許接收字符串(可以將Type統一設置爲String)。
在這裏插入圖片描述

3.內容特殊字符,導致解析報錯

在這裏插入圖片描述

這有可能是內容裏面存在特殊符號,在模板生成文件時被xml解析器解析,導致的問題。

**解決辦法:**使用<![CDATA[ ]]> 包裹渲染的數據, CDATA 區段(CDATA section)中的文本會被解析器忽略。
在這裏插入圖片描述

最後

以上內容爲個人總結備用,如有問題,請幫忙指正,感謝。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章