SpringBoot圖文教程10—模板導出|百萬數據Excel導出|圖片導出「easypoi」
- 2020 年 3 月 5 日
- 筆記
有天上飛的概念,就要有落地的實現
概念十遍不如程式碼一遍,朋友,希望你把文中所有的程式碼案例都敲一遍
先贊後看,養成習慣
SpringBoot 圖文教程系列文章目錄
- SpringBoot圖文教程1「概念+案例 思維導圖」「基礎篇上」
- SpringBoot圖文教程2—日誌的使用「logback」「log4j」
- SpringBoot圖文教程3—「『初戀』情結」集成Jsp
- SpringBoot圖文教程4—SpringBoot 實現文件上傳下載
- SpringBoot圖文教程5—SpringBoot 中使用Aop
- SpringBoot圖文教程6—SpringBoot中過濾器的使用
- SpringBoot圖文教程7—SpringBoot攔截器的使用姿勢這都有
- SpringBoot圖文教程8 — SpringBoot集成MBG「程式碼生成器」
- SpringBoot圖文教程9—SpringBoot 導入導出 Excel 「Apache Poi」
前言
上一篇文章中簡單介紹了Poi的使用方式,但是用Poi去寫程式碼著實繁瑣了一些,假如你要實現的是複雜的需求,譬如:圖片導出,多表數據導出,模板導出,大數據量導出等等,用最原生的Poi就不是很好的選擇了。
難道要自己封裝工具類了嗎?
no no 輪子雖好,最好是別人寫好的。所以從這篇文章開始介紹兩個優秀的Poi工具 Easypoi 和 阿里開源的 EasyExcel。
EasyPoi
EasyPoi 是對poi封裝的一個工具庫,封裝好了一些常見的Excel操作
- 最基本的導入導出
- 圖片的導入導出
- 多表數據的導入導出
- 大批量數據的導入導出
- 模板的導出
接下來我們一起來將以上的功能實現出來
最基本的導入導出
最基本的導入導出,要導出的數據的實體類如下:
public class Teacher { /** * 老師的主鍵 */ private Integer teacherId; /** * 名字 */ private String teacherName; /** * 頭像圖片地址 */ private String teacherImage; /** * 老師的狀態 0代表正常 1代表刪除 */ private Integer teacherStatus; }
省略get set
1.導入依賴
<dependency> <groupId>cn.afterturn</groupId> <artifactId>easypoi-base</artifactId> <version>3.2.0</version> </dependency> <dependency> <groupId>cn.afterturn</groupId> <artifactId>easypoi-web</artifactId> <version>3.2.0</version> </dependency> <dependency> <groupId>cn.afterturn</groupId> <artifactId>easypoi-annotation</artifactId> <version>3.2.0</version> </dependency>
2.給要導出數據實體類加註解
我們要導出的數據的實體類是Teacher 所以需要給Teacher加 Easypoi的註解
EasyPoi是註解式開發,所有的註解詳情見官方文檔,但是如果要完成的需求是最簡單的導入導出的話,@Excel
一個註解就足夠了。
@Excel
這個是必須使用的註解,如果需求簡單只使用這一個註解也是可以的,涵蓋了常用的Excel需求,需要大家熟悉這個功能,主要分為基礎,圖片處理,時間處理,合併處理幾塊
@Excel 的官方api http://easypoi.mydoc.io/#text_186900
添加好註解的實體類如下:
3.直接使用 EasyPoi 中的工具類導入導出
EasyPoi 是註解式開發,對Excel所有的定義,樣式也好,日期格式化也好,都是在實體類的註解中定義
導出程式碼
/** * easypoi導出 */ @Test public void test4() throws IOException { // 模擬數據 List<Teacher> list = new ArrayList<>(); list.add(new Teacher(1,"李老師","hhh.jpg",1)); list.add(new Teacher(2,"李老師","hhh.jpg",1)); list.add(new Teacher(3,"李老師","hhh.jpg",1)); list.add(new Teacher(4,"李老師","hhh.jpg",1)); list.add(new Teacher(5,"李老師","hhh.jpg",1)); list.add(new Teacher(6,"李老師","hhh.jpg",1)); /** * 導出參數對象 * 參數1 標題 * 參數2 表的名字 */ ExportParams exportParams = new ExportParams("所有老師數據","teacher"); /** * exportExcel 導出Excel文件 * 參數1 導出參數對象 * 參數2 要導出的實體類的類對象 * 參數3 要導出的數據 需要一個集合 資料庫查詢出來的老師對象的集合 * * 返回值就是封裝好的文件對象 */ Workbook workbook = ExcelExportUtil.exportExcel(exportParams, Teacher.class, list); workbook.write(new FileOutputStream("/Users/lubingyang/Desktop/teachers.xls")); }
導出之後的Excel如下:
導入程式碼
關於導入 沒有需要單獨定義的配置 只要能夠導出就能夠直接寫導入的程式碼
/** * easypoi導入 */ @Test public void test5() throws Exception { FileInputStream inputStream = new FileInputStream("/Users/lubingyang/Desktop/teachers.xls"); /** * ImportParams 導入參數對象 * 定義標題欄和表頭數據 */ ImportParams importParams = new ImportParams(); importParams.setTitleRows(1); importParams.setHeadRows(1); /** * importExcel 導入方法 * 參數1 流 讀取要導入的文件 * 參數2 要導入的實體類的類對象 上師對象的類對象 * 參數3 導入參數對象 * * 返回值 導入數據 直接封裝為集合對象 */ List<Teacher> teachers = ExcelImportUtil.importExcel(inputStream, Teacher.class, importParams); for (Teacher teacher : teachers) { System.out.println(teacher); } }
值的替換 圖片導入導出
值的替換
通過官方文檔很容易找到如下內容
根據文檔修改實體類
執行導出程式碼 可以得到如下效果
圖片導出
修改實體類
修改完之後可以直接導出
根據測試,如果圖片地址欄位存儲的是相對路徑,最好處理為網路絕對絕對路徑或者本地絕對路徑
圖片導入
在實體類的註解上需要設置圖片導入之後的保存路徑
api
集合數據導入導出
在增加一個實體類 Student ,在Teacher類中有一個學生集合,導出Teacher的同時需要將Student的數據也導出,對應的資料庫操作一般都是連表查詢,那麼這樣的數據怎麼導出到Excel呢?
實體類加註解
針對 Teacher 類集合屬性的導入導出,需要給該屬性加註解 @ExcelConllection
官方文檔地址:http://easypoi.mydoc.io/#text_197841
導出程式碼如下
@Test public void test4() throws IOException { List<Student> students = new ArrayList<>(); students.add(new Student("hh","男")); students.add(new Student("hh","男")); // 模擬數據 List<Teacher> list = new ArrayList<>(); list.add(new Teacher(1,"李老師","/Users/lubingyang/Desktop/hhh.jpg",1,students)); list.add(new Teacher(6,"李老師","/Users/lubingyang/Desktop/hhh.jpg",1,students)); /** * 導出參數對象 * 參數1 標題 * 參數2 表的名字 */ ExportParams exportParams = new ExportParams("所有老師數據","teacher"); /** * exportExcel 導出Excel文件 * 參數1 導出參數對象 * 參數2 要導出的實體類的類對象 * 參數3 要導出的數據 需要一個集合 資料庫查詢出來的老師對象的集合 * * 返回值就是封裝好的文件對象 */ Workbook workbook = ExcelExportUtil.exportExcel(exportParams, Teacher.class, list); workbook.write(new FileOutputStream("/Users/lubingyang/Desktop/teachers.xls")); }
效果如下
大批量數據導出(百萬數據)
關於百萬數據導出 推薦使用 阿里開源的 EasyExcel 官方介紹可以將記憶體控制在kb
大數據導出是當我們的導出數量在幾萬,到上百萬的數據時,一次從資料庫查詢這麼多數據載入到記憶體,然後寫入會對我們的記憶體和CPU都產生壓力,這個時候需要我們像分頁一樣處理導出,分段寫入Excel緩解壓力
EasyPoi提供的是兩個方法 強制使用 xssf版本的Excel
/** * @param entity * 表格標題屬性 * @param pojoClass * Excel對象Class * @param dataSet * Excel對象數據List */ public static Workbook exportBigExcel(ExportParams entity, Class<?> pojoClass, Collection<?> dataSet) { ExcelBatchExportServer batachServer = ExcelBatchExportServer .getExcelBatchExportServer(entity, pojoClass); return batachServer.appendData(dataSet); } public static void closeExportBigExcel() { ExcelBatchExportServer batachServer = ExcelBatchExportServer.getExcelBatchExportServer(null, null); batachServer.closeExportBigExcel(); }
思路
- 分頁讀取數據
- 將每次讀取到的數據寫入Excel
實現程式碼
準備一個百萬數據的用戶表
@Test public void test10() throws IOException { Date start = new Date(); // 查詢資料庫 用戶表總條數 Integer userCount = userDao.selectCount(null); // 計算總頁數 Integer pageCount = userCount / 200000 + 1; List<CmfzUser> users = null; Workbook workbook = null; ExportParams params = new ExportParams("大數據測試", "測試"); // 查詢測試 頁數 每次查詢20w條數據 for (int i = 1; i <= pageCount; i++) { System.out.println(i); users = userDao.selectPage(new Page<>(i, 200000), null).getRecords(); // 通過 EasyPoi 的大數據導出方法 導出 workbook = ExcelExportUtil.exportBigExcel(params, CmfzUser.class, users); users.clear(); } Date end = new Date(); System.out.println(new Date().getTime() - start.getTime()); workbook.write(new FileOutputStream("/Users/lubingyang/Desktop/hhhh.xlsx")); }
執行的總時間為:
Tips:
- 時間問題沒做詳細的性能測試 官方有相關的測試:http://easypoi.mydoc.io/#text_202983
- 資料庫查詢使用MybatisPlus 如果有興趣 可以看我的相關文章 SpringBoot 集成 MybatisPlus
- 百萬數據用 xssf 不如使用 CSV 和 SXSSF( POI針對大數據量的導出,專門提供了一個類)
模板導出
模板是處理複雜Excel的簡單方法,複雜的Excel樣式,可以用Excel直接編輯,完美的避開了程式碼編寫樣式的雷區,同時指令的支援,也提了模板的有效性。
EasyPoi支援的指令以及作用
空格分割 三目運算 {{test ? obj:obj2}} n: 表示 這個cell是數值類型 {{n:}} le: 代表長度{{le:()}} 在if/else 運用{{le:() > 8 ? obj1 : obj2}} fd: 格式化時間 {{fd:(obj;yyyy-MM-dd)}} fn: 格式化數字 {{fn:(obj;###.00)}} fe: 遍曆數據,創建row !fe: 遍曆數據不創建row $fe: 下移插入,把當前行,下面的行全部下移.size()行,然後插入 #fe: 橫向遍歷 v_fe: 橫向遍歷值 !if: 刪除當前列 {{!if:(test)}} 單引號表示常量值 '' 比如'1' 那麼輸出的就是 1 &NULL& 空格 ]] 換行符 多行遍歷導出 sum: 統計數據
採用的寫法是{{}}代表表達式,然後根據表達式裡面的數據取值
程式碼如下
示例程式碼所用模板文件地址:
@Test public void fe_map() throws Exception { // 讀取模板文件 TemplateExportParams params = new TemplateExportParams( "/Users/k/Desktop/專項支出用款申請書_map.xls"); // 模擬要寫入模板的數據 Map<String, Object> map = new HashMap<String, Object>(); map.put("date", "2014-12-25"); map.put("money", 2000000.00); map.put("upperMoney", "貳佰萬"); map.put("company", "執筆潛行科技有限公司"); map.put("bureau", "財政局"); map.put("person", "JueYue"); map.put("phone", "1879740****"); List<Map<String, String>> listMap = new ArrayList<Map<String, String>>(); for (int i = 0; i < 4; i++) { Map<String, String> lm = new HashMap<String, String>(); lm.put("id", i + 1 + ""); lm.put("zijin", i * 10000 + ""); lm.put("bianma", "A001"); lm.put("mingcheng", "設計"); lm.put("xiangmumingcheng", "EasyPoi " + i + "期"); lm.put("quancheng", "開源項目"); lm.put("sqje", i * 10000 + ""); lm.put("hdje", i * 10000 + ""); listMap.add(lm); } map.put("maplist", listMap); // 導出模板 Workbook workbook = ExcelExportUtil.exportExcel(params, map); FileOutputStream fos = new FileOutputStream("/Users/k/Desktop/專項支出用款申請書111_map.xls"); workbook.write(fos); fos.close(); }
結果如下
總結
Tips:通過EasyPoi 基本上已經可以完成所有的Excel相關的工作
可以關注相關文章 poi 和 EasyExcel 以及 Poi中文 API 文檔 「40種操作 Excel文件的姿勢」
恭喜你完成了本章的學習,為你鼓掌!如果本文對你有幫助,請幫忙點贊,評論,轉發,這對作者很重要,謝謝。
讓我們再次回顧本文的學習目標
- 掌握SpringBoot中Easypoi的使用
要掌握SpringBoot更多的用法,請持續關注本系列教程。
求關注,求點贊,求轉發
歡迎關注本人公眾號:鹿老師的Java筆記,將在長期更新Java技術圖文教程和影片教程,Java學習經驗,Java面試經驗以及Java實戰開發經驗。