Java使用EasyExcel模版導(dǎo)出詳細(xì)操作教程
一、官方提供方法
easyexcel官方文檔 填充Excel | Easy Excel
官方demo是利用本地模版文件填充并下載到本地
/** * 復(fù)雜的填充 * * @since 2.1.1 */ @Test public void complexFill() { // 模板注意 用{} 來表示你要用的變量 如果本來就有"{","}" 特殊字符 用"\{","\}"代替 // {} 代表普通變量 {.} 代表是list的變量 String templateFileName = TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "complex.xlsx"; String fileName = TestFileUtil.getPath() + "complexFill" + System.currentTimeMillis() + ".xlsx"; // 方案1 try (ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build()) { WriteSheet writeSheet = EasyExcel.writerSheet().build(); // 這里注意 入?yún)⒂昧薴orceNewRow 代表在寫入list的時候不管list下面有沒有空行 都會創(chuàng)建一行,然后下面的數(shù)據(jù)往后移動。默認(rèn) 是false,會直接使用下一行,如果沒有則創(chuàng)建。 // forceNewRow 如果設(shè)置了true,有個缺點 就是他會把所有的數(shù)據(jù)都放到內(nèi)存了,所以慎用 // 簡單的說 如果你的模板有l(wèi)ist,且list不是最后一行,下面還有數(shù)據(jù)需要填充 就必須設(shè)置 forceNewRow=true 但是這個就會把所有數(shù)據(jù)放到內(nèi)存 會很耗內(nèi)存 // 如果數(shù)據(jù)量大 list不是最后一行 參照下一個 FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); excelWriter.fill(data(), fillConfig, writeSheet); excelWriter.fill(data(), fillConfig, writeSheet); Map<String, Object> map = MapUtils.newHashMap(); map.put("date", "2019年10月9日13:28:28"); map.put("total", 1000); excelWriter.fill(map, writeSheet); } }
二、根據(jù)業(yè)務(wù)場景的方法
我用的是web項目,將輸出改為了輸出流 OutputStream
/** * 導(dǎo)出質(zhì)檢任務(wù)詳情 * * @param id 任務(wù)主鍵,用于獲取數(shù)據(jù) */ @Override public void exportTask(Long id, HttpServletResponse response) { ExcelWriter excelWriter = null; try { // outputStream:要導(dǎo)出的文件的輸出流 OutputStream outputStream = response.getOutputStream(); // 模版文件 ClassPathResource classPathResource = new ClassPathResource("template/taskTemplate.xlsx"); // 使用模版文件的兩種方式: // 1、文件路徑:.withTemplate(templateFileName) // 2、輸入流:.withTemplate(inputStream) String templateFileName = classPathResource.getFile().getPath(); InputStream inputStream = classPathResource.getInputStream(); excelWriter = EasyExcel.write(outputStream).withTemplate(inputStream).excelType(ExcelTypeEnum.XLSX).autoCloseStream(Boolean.FALSE).build(); WriteSheet writeSheet = EasyExcel.writerSheet().build(); // 獲取數(shù)據(jù) dataList columnList formData Map<String, Object> mapDetail = qmsQcTaskDetailService.getDataListByTaskId(id); // 調(diào)用微服務(wù)獲取字典 determine 用來翻譯判定結(jié)果 List<SysDictItem> determineItem = remoteDictService.getDictByType("determine").getData(); // 調(diào)用微服務(wù)獲取字典 product_unit 用來翻譯零件單位 List<SysDictItem> unitItem = remoteDictService.getDictByType("product_unit").getData(); /* 1、List以外的數(shù)據(jù) */ QmsQcTask formData = (QmsQcTask) mapDetail.get("formData"); determineItem.stream().filter(item -> StrUtil.equals(formData.getResult(), item.getItemValue())).findFirst().ifPresent(sysDictItem -> formData.setResult(sysDictItem.getLabel())); unitItem.stream().filter(item -> StrUtil.equals(formData.getMaterialUnit(), item.getItemValue())).findFirst().ifPresent(sysDictItem -> formData.setMaterialUnit(sysDictItem.getLabel())); excelWriter.fill(formData, writeSheet); /* 2、List cols數(shù)據(jù) */ List<Map<String, Object>> cols = new ArrayList<>(); // 查找抽檢數(shù)是多少,即有幾列 int num = formData.getSampleNum(); for (int i = 1; i <= num; i++) { Map<String, Object> map = new HashMap<>(); // 列名 map.put("label", "" + i); cols.add(map); } // 橫向填充 FillConfig fillConfigCols = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build(); excelWriter.fill(new FillWrapper("cols", cols), fillConfigCols, writeSheet); /* 3、List details數(shù)據(jù) */ // 這里注意 入?yún)⒂昧薴orceNewRow 代表在寫入list的時候不管list下面有沒有空行 都會創(chuàng)建一行,然后下面的數(shù)據(jù)往后移動。默認(rèn) 是false,會直接使用下一行,如果沒有則創(chuàng)建。 // forceNewRow 如果設(shè)置了true,有個缺點 就是他會把所有的數(shù)據(jù)都放到內(nèi)存了,所以慎用 // 簡單的說 如果你的模板有l(wèi)ist,且list不是最后一行,下面還有數(shù)據(jù)需要填充 就必須設(shè)置 forceNewRow=true 但是這個就會把所有數(shù)據(jù)放到內(nèi)存 會很耗內(nèi)存 FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); List<Map<String, Object>> dataList = (List<Map<String, Object>>) mapDetail.get("dataList"); for (int i = 0; i < dataList.size(); i++) { Map<String, Object> data = dataList.get(i); // 翻譯判定合不合格(因為業(yè)務(wù)和字典不在一個數(shù)據(jù)庫,所以放在代碼里處理) String determine = (String) data.get("determine"); determineItem.stream().filter(item -> StrUtil.equals(determine, item.getItemValue())).findFirst().ifPresent(sysDictItem -> data.put("determine", sysDictItem.getLabel())); // 如果沒有質(zhì)檢標(biāo)準(zhǔn),每一個質(zhì)檢值都要轉(zhuǎn)為合不合格 if (StrUtil.isBlank((String) data.get("inspection_standard"))){ int samplingNum = Optional.ofNullable((Integer) data.get("sampling_num")).orElse(0); for (int j = 1; j <= samplingNum; j++) { String val = (String) data.get(""+j); int finalJ = j; determineItem.stream().filter(item -> StrUtil.equals(val, item.getItemValue())).findFirst().ifPresent(sysDictItem -> data.put(""+ finalJ, sysDictItem.getLabel())); } } // 給數(shù)據(jù)加上序號 data.put("index", i + 1); } excelWriter.fill(new FillWrapper("details", dataList), fillConfig, writeSheet); // 設(shè)置輸出流格式以及文件名: response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setCharacterEncoding("utf-8"); String fileName = URLEncoder.encode("質(zhì)檢任務(wù)", "UTF-8").replaceAll("\\+", "%20"); response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); } catch (IOException e) { throw new RuntimeException(e); } finally { // 千萬別忘記close關(guān)閉流 if (excelWriter != null) { excelWriter.close(); } } }
模版:
效果:
三、說明
1、獲取輸出流
// outputStream:要導(dǎo)出的文件的輸出流 OutputStream outputStream = response.getOutputStream();
2、獲取模版文件
// 模版文件 ClassPathResource classPathResource = new ClassPathResource("template/taskTemplate.xlsx"); // 方式一:路徑 String templateFileName = classPathResource.getFile().getPath(); // 方式二:輸入流 InputStream inputStream = classPathResource.getInputStream();
3、創(chuàng)建 ExcelWriter 和 WriteSheet
excelWriter = EasyExcel.write(outputStream).withTemplate(inputStream).excelType(ExcelTypeEnum.XLSX).autoCloseStream(Boolean.FALSE).build(); WriteSheet writeSheet = EasyExcel.writerSheet().build();
使用模版文件有兩種方式,使用其中一種就可以:
1.文件路徑:.withTemplate(templateFileName)
2.輸入流:.withTemplate(inputStream)
4、獲取數(shù)據(jù)
Map<String, Object> mapDetail = qmsQcTaskDetailService.getDataListByTaskId(id);
我這里直接調(diào)用了方法,map包含
dataList主要列表數(shù)據(jù) formData 列表外的數(shù)據(jù)
5、填充數(shù)據(jù)
1.獲取字典數(shù)據(jù)
// 調(diào)用微服務(wù)獲取字典 determine 用來翻譯判定結(jié)果 List<SysDictItem> determineItem = remoteDictService.getDictByType("determine").getData(); // 調(diào)用微服務(wù)獲取字典 product_unit 用來翻譯零件單位 List<SysDictItem> unitItem = remoteDictService.getDictByType("product_unit").getData();
我的業(yè)務(wù)數(shù)據(jù)和字典不在同一個數(shù)據(jù)庫內(nèi),sql無法翻譯,所以需要在代碼里翻譯
2.填充列表外的數(shù)據(jù)
/* 1、List以外的數(shù)據(jù) */ QmsQcTask formData = (QmsQcTask) mapDetail.get("formData"); determineItem.stream().filter(item -> StrUtil.equals(formData.getResult(), item.getItemValue())).findFirst().ifPresent(sysDictItem -> formData.setResult(sysDictItem.getLabel())); unitItem.stream().filter(item -> StrUtil.equals(formData.getMaterialUnit(), item.getItemValue())).findFirst().ifPresent(sysDictItem -> formData.setMaterialUnit(sysDictItem.getLabel())); excelWriter.fill(formData, writeSheet);
只要對象屬性名和模版里寫的標(biāo)記已知就可以了
3.填充缺少的列名
模版:
效果:
因為我后半部分列名涉及到行轉(zhuǎn)列,且不固定列數(shù),所以需要橫著填充上列名。注意列表的名字“cols”要對應(yīng)起來。
使用.direction(WriteDirectionEnum.HORIZONTAL) 設(shè)置為橫向填充。
/* 2、List cols數(shù)據(jù) */ List<Map<String, Object>> cols = new ArrayList<>(); // 查找抽檢數(shù)是多少,即有幾列 int num = formData.getSampleNum(); for (int i = 1; i <= num; i++) { Map<String, Object> map = new HashMap<>(); // 列名 map.put("label", "" + i); cols.add(map); } // 橫向填充 FillConfig fillConfigCols = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build(); excelWriter.fill(new FillWrapper("cols", cols), fillConfigCols, writeSheet);
4.填充主列表數(shù)據(jù)
/* 3、List details數(shù)據(jù) */ // 這里注意 入?yún)⒂昧薴orceNewRow 代表在寫入list的時候不管list下面有沒有空行 都會創(chuàng)建一行,然后下面的數(shù)據(jù)往后移動。默認(rèn) 是false,會直接使用下一行,如果沒有則創(chuàng)建。 // forceNewRow 如果設(shè)置了true,有個缺點 就是他會把所有的數(shù)據(jù)都放到內(nèi)存了,所以慎用 // 簡單的說 如果你的模板有l(wèi)ist,且list不是最后一行,下面還有數(shù)據(jù)需要填充 就必須設(shè)置 forceNewRow=true 但是這個就會把所有數(shù)據(jù)放到內(nèi)存 會很耗內(nèi)存 FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); List<Map<String, Object>> dataList = (List<Map<String, Object>>) mapDetail.get("dataList"); /*以下為個人業(yè)務(wù)處理*/ for (int i = 0; i < dataList.size(); i++) { Map<String, Object> data = dataList.get(i); // 翻譯判定合不合格(因為業(yè)務(wù)和字典不在一個數(shù)據(jù)庫,所以放在代碼里處理) String determine = (String) data.get("determine"); determineItem.stream().filter(item -> StrUtil.equals(determine, item.getItemValue())).findFirst().ifPresent(sysDictItem -> data.put("determine", sysDictItem.getLabel())); // 如果沒有質(zhì)檢標(biāo)準(zhǔn),每一個質(zhì)檢值都要轉(zhuǎn)為合不合格 if (StrUtil.isBlank((String) data.get("inspection_standard"))){ int samplingNum = Optional.ofNullable((Integer) data.get("sampling_num")).orElse(0); for (int j = 1; j <= samplingNum; j++) { String val = (String) data.get(""+j); int finalJ = j; determineItem.stream().filter(item -> StrUtil.equals(val, item.getItemValue())).findFirst().ifPresent(sysDictItem -> data.put(""+ finalJ, sysDictItem.getLabel())); } } // 給數(shù)據(jù)加上序號 data.put("index", i + 1); } /*以上為個人業(yè)務(wù)處理*/ excelWriter.fill(new FillWrapper("details", dataList), fillConfig, writeSheet);
最重要的三句話:
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); List<Map<String, Object>> dataList = (List<Map<String, Object>>) mapDetail.get("dataList"); excelWriter.fill(new FillWrapper("details", dataList), fillConfig, writeSheet);
這里注意 入?yún)⒂昧薴orceNewRow 代表在寫入list的時候不管list下面有沒有空行 都會創(chuàng)建一行,然后下面的數(shù)據(jù)往后移動。默認(rèn) 是false,會直接使用下一行,如果沒有則創(chuàng)建。forceNewRow 如果設(shè)置了true,有個缺點 就是他會把所有的數(shù)據(jù)都放到內(nèi)存了,所以慎用簡單的說 如果你的模板有l(wèi)ist,且list不是最后一行,下面還有數(shù)據(jù)需要填充 就必須設(shè)置 forceNewRow=true 但是這個就會把所有數(shù)據(jù)放到內(nèi)存 會很耗內(nèi)存
5.設(shè)置輸出流格式以及文件名
// 設(shè)置輸出流格式以及文件名: response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setCharacterEncoding("utf-8"); String fileName = URLEncoder.encode("質(zhì)檢任務(wù)", "UTF-8").replaceAll("\\+", "%20"); response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
四、思想
我的模版使用了動態(tài)的列,不確定有幾列,所以我就寫了一百多個。
還有另外一種設(shè)想:
如果先把列表外的數(shù)據(jù)填充,然后將主列表的列填充完畢,再把主列表的標(biāo)記寫上,這些操作完畢之后把文件輸出到另外的輸出流上,再將此輸出流轉(zhuǎn)為輸入流,再把主列表的數(shù)據(jù)填充,輸出到response。
理論上可行,但我沒試。
總結(jié)
到此這篇關(guān)于Java使用EasyExcel模版導(dǎo)出的文章就介紹到這了,更多相關(guān)使用EasyExcel模版導(dǎo)出內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringCloud網(wǎng)關(guān)(Zuul)如何給多個微服務(wù)之間傳遞共享參數(shù)
這篇文章主要介紹了SpringCloud網(wǎng)關(guān)(Zuul)如何給多個微服務(wù)之間傳遞共享參數(shù),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03Java的四種常見線程池及Scheduled定時線程池實現(xiàn)詳解
這篇文章主要介紹了Java的四種常見線程池及Scheduled定時線程池實現(xiàn)詳解,在Java中,我們可以通過Executors類來創(chuàng)建ScheduledThreadPool,Executors類提供了幾個靜態(tài)方法來創(chuàng)建不同類型的線程池,包括ScheduledThreadPool,需要的朋友可以參考下2023-09-09idea中maven使用tomcat7插件運行run報錯Could not start T
這篇文章主要介紹了idea中maven使用tomcat7插件運行run報錯Could not start Tomcat問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-09-09MyBatis中的關(guān)聯(lián)關(guān)系配置與多表查詢的操作代碼
本文介紹了在MyBatis中配置和使用一對多和多對多關(guān)系的方法,通過合理的實體類設(shè)計、Mapper接口和XML文件的配置,我們可以方便地進(jìn)行多表查詢,并豐富了應(yīng)用程序的功能和靈活性,需要的朋友可以參考下2023-09-09java學(xué)習(xí)筆記之eclipse+tomcat 配置
俗話說:工欲善其事必先利其器,既然要學(xué)習(xí)java,首先把java的開發(fā)環(huán)境搗鼓一下吧,這里我們來談?wù)別clipse+tomcat的配置方法。2014-11-11SpringBoot3實戰(zhàn)教程之實現(xiàn)接口簽名驗證功能
接口簽名是一種重要的安全機制,用于確保 API 請求的真實性、數(shù)據(jù)的完整性以及防止重放攻擊,這篇文章主要介紹了SpringBoot3實戰(zhàn)教程之實現(xiàn)接口簽名驗證功能,需要的朋友可以參考下2025-04-04Java動態(tài)修改配置即時生效的方式WatchService
這篇文章給大家分享了Java動態(tài)修改配置即時生效的方式WatchService的相關(guān)知識點內(nèi)容,有興趣的朋友可以參考學(xué)習(xí)下。2018-06-06