前言
導出excel是比較常見的需求,有時候需要導出的excel有固定的格式,這裏記錄一下FreeMarker導出excel,暫時不瞭解FreeMarker基本語法的請先去學習一下。
正文
FreeMarker是一款模板引擎: 即一種基於模板和要改變的數據, 並用來生成輸出文本(HTML網頁、電子郵件、配置文件、源代碼等)的通用工具。 它不是面向最終用戶的,而是一個Java類庫,是一款程序員可以嵌入他們所開發產品的組件。(百度百科)
FreeMarker導出的步驟大概就三步:製作模板–>取數據–>渲染導出excel。
它能導出的不限於excel,上面說了FreeMarker是一個模板引擎,能夠生成各類文件,大概如下:
製作模板
製作模板,以excel爲例,先將有格式的excel另存爲xml格式的文件
實際中需求的格式可能比較複雜,但是沒有關係,因爲製作模板不會對其樣式進行操作,只涉及修改數據填充的部分,所以樣式不管再複雜,都是一樣的。
將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)中的文本會被解析器忽略。
最後
以上內容爲個人總結備用,如有問題,請幫忙指正,感謝。