EasyExcel進(jìn)階之一個(gè)填充模版動(dòng)態(tài)生成多個(gè)Sheet頁(yè)
一、前言
今天收到一個(gè)導(dǎo)出Excel的需求,這種需求經(jīng)常做,看到模版還是是有點(diǎn)復(fù)雜的有頭有行,一般的導(dǎo)出是不好做,使用模板填充比較簡(jiǎn)單!另外還有一個(gè)需求小編是第一次見(jiàn),所以來(lái)記錄一下,為后來(lái)人鋪路!
需求:導(dǎo)出有單子的頭信息和一些多個(gè)行信息,前端可以多選,「多個(gè)放在一個(gè)excel里的sheet中」
明白了需求我們技術(shù)選型,現(xiàn)在基本都是EasyExcel用的比較多,今天使用的版本為:3.1.5,低版本已經(jīng)不維護(hù),建議使用高點(diǎn)的版本哈!
「在這里先說(shuō)一下,EasyExcel單獨(dú)是無(wú)法實(shí)現(xiàn)一個(gè)模版動(dòng)態(tài)填充多個(gè)sheet頁(yè),所以我們使用POI來(lái)幫忙復(fù)制sheet頁(yè)即可!」
二、準(zhǔn)備工作
1、閹割版效果圖
我簡(jiǎn)化了一些功能,自己做了一個(gè)簡(jiǎn)單的模板,但是功能都是有的,大致如下圖所示:
「多個(gè)就使用合同號(hào)作為sheet名稱(chēng),每個(gè)里面也會(huì)有多個(gè)行垂直填充即可!」
2、填充模版
3、導(dǎo)入依賴(lài)
中間使用了Hutool來(lái)獲取流,很多公司不讓使用Hutool的,大家自己選擇,不使用可以用:
- Spring的ResourceUtils.getFile()
- JDK的new File()
本次使用Hutool的ClassPathResource。
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.1.5</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.4</version>
</dependency>
4、導(dǎo)出實(shí)體
/**
* @author wangzhenjun
* @date 2023/7/4 17:09
*/
@Data
public class TestExcel {
private String contractNo;
private String address;
private String dateTime;
private List<Item> itemList;
@Data
public static class Item{
private String name;
private BigDecimal price;
}
}
三、實(shí)戰(zhàn)代碼
為了方便直接寫(xiě)在Controller里了,大家不要學(xué)習(xí)哈,業(yè)務(wù)的處理還是要在service里寫(xiě)!
整體思路:
使用POI的XSSFWorkbook 來(lái)根據(jù)要導(dǎo)出的個(gè)數(shù)來(lái)進(jìn)行「復(fù)制sheet頁(yè)」,名稱(chēng)為合同號(hào)
將復(fù)制好的sheet頁(yè)轉(zhuǎn)換成字節(jié)數(shù)組,然后再通過(guò)輸入流的方式讀取字節(jié)數(shù)組中的數(shù)據(jù)。
EasyExcel 將使用輸入流中的模板數(shù)據(jù)生成 Excel 數(shù)據(jù),并將生成的 Excel 文件寫(xiě)入到 HttpServletResponse 的輸出流中,以便將其發(fā)送給客戶(hù)端進(jìn)行下載或其他處理。
創(chuàng)建一個(gè) WriteSheet 對(duì)象,并將其與上面復(fù)制的sheet頁(yè)名稱(chēng)關(guān)聯(lián),就可以往里面填充數(shù)據(jù)了!
如果有這種list多個(gè)行填充的并且list不是最后一行,下面還有數(shù)據(jù)需要填充 就必須設(shè)置 forceNewRow=true
不加的話(huà)會(huì)把你后面的內(nèi)容給覆蓋了!
但是這個(gè)就會(huì)把所有數(shù)據(jù)放到內(nèi)存 會(huì)很耗內(nèi)存!
「用完記得把流關(guān)閉哈!」
@SneakyThrows
@GetMapping("/excel")
public void excel (HttpServletResponse response){
int size = 2;
List<TestExcel> testExcels = new ArrayList<>();
for (int i = 0; i < size; i++) {
TestExcel testExcel = new TestExcel();
testExcel.setContractNo("HT07040" + (i + 1));
testExcel.setAddress("青島" + i + "號(hào)基地");
testExcel.setDateTime("2023-07-05");
testExcels.add(testExcel);
List<TestExcel.Item> itemList = new ArrayList<>();
for (int j = 0; j < size; j++) {
TestExcel.Item item = new TestExcel.Item();
item.setName("商品" + (j + 1));
item.setPrice(new BigDecimal("188").multiply(new BigDecimal(j + 1)));
itemList.add(item);
}
testExcel.setItemList(itemList);
}
response.setCharacterEncoding("utf-8");
String fileName = URLEncoder.encode("測(cè)試", "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
ClassPathResource classPathResource = new ClassPathResource("template" + File.separator + "測(cè)試.xlsx");
InputStream stream = classPathResource.getStream();
// 把excel流給這個(gè)對(duì)象,后續(xù)可以操作
XSSFWorkbook workbook = new XSSFWorkbook(stream);
// 設(shè)置模板的第一個(gè)sheet的名稱(chēng),名稱(chēng)我們使用合同號(hào)
workbook.setSheetName(0, testExcels.get(0).getContractNo());
for (int i = 1; i < size; i++) {
// 剩余的全部復(fù)制模板sheet0即可
workbook.cloneSheet(0, testExcels.get(i).getContractNo());
}
// 把workbook寫(xiě)到流里
ByteArrayOutputStream baos = new ByteArrayOutputStream();
workbook.write(baos);
byte[] bytes = baos.toByteArray();
stream = new ByteArrayInputStream(bytes);
ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).withTemplate(stream).build();
for (TestExcel testExcel : testExcels) {
WriteSheet writeSheet = EasyExcel.writerSheet(testExcel.getContractNo()).build();
// list不是最后一行,下面還有數(shù)據(jù)需要填充 就必須設(shè)置 forceNewRow=true 但是這個(gè)就會(huì)把所有數(shù)據(jù)放到內(nèi)存 會(huì)很耗內(nèi)存
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).direction(WriteDirectionEnum.VERTICAL).build();
excelWriter.fill(testExcel, writeSheet);
excelWriter.fill(new FillWrapper("item", testExcel.getItemList()), fillConfig, writeSheet);
}
excelWriter.finish();
baos.close();
stream.close();
}
四、總結(jié)
這樣就完成了,主要的難點(diǎn)是復(fù)制sheet頁(yè),多行填充在EasyExcel官網(wǎng)都是有的,還有一些我這邊沒(méi)有用到的東西,大家可以根據(jù)自己的需求去找找看!
EasyExcel填充地址:https://easyexcel.opensource.alibaba.com/docs/current/quickstart/fill。