文章目录
- 1.业务背景
- 2.解决思路
- 3.解决方案
- 3.1 引入依赖
- 3.2 详细实现
- 3.3 结果测试
- 4.优缺分析
1.业务背景
最近公司有一个业务就是要求导出一个excel,虽说这个excel的表头只有一行,但是这一行的表头是由两部分组成,
一部分是固定部分,一部分是动态部分。要求导出的表头如下图所示,该表中前五列是固定表头,后面的列数不固定,
有可能是五个月,六个月或者八个月,九个月都有可能。
2.解决思路
因为笔者目前只掌握了Easyexcel的固定表头(也就是固定列)的导出,所以这里没有像用easy excel一样把该
excel每一行的数据抽象成一个对象去处理。所以这里用到的技术是poi,然后主要是通过哪一行哪一列来确定一个
单元格,比如一行一列确定了一个单元格,然后把该行该列的数据作为条件去查对应单元格里面的数据。
3.解决方案
3.1 引入依赖
//因为easyexcel底层用的也是poi,所以引入这个类就等于引入了poi
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.1.3</version>
</dependency>
3.2 详细实现
public void dateConvertDemoFour(HttpServletResponse response) throws IOException {
//经过查阅相关资料,从HSSFWorkbook和XSSFWorkbook还有SXSSFWorkbook中选出了SXSSFWorkbook作为适合笔者需求的实现类
int rowAccess = 100;
SXSSFWorkbook wb = new SXSSFWorkbook(rowAccess);
//设置表头样式
CellStyle cellStyleHeader = getCellStyle(wb);
//创建第一个sheet
SXSSFSheet sheetOne = wb.createSheet();
wb.setSheetName(0, "第一个sheet页");
//创建第二个sheet
Sheet sheetTwo = wb.createSheet();
wb.setSheetName(1, "第二个sheet页");
/**
* 一、确定第一个sheet表头(第二个sheet类似)
*/
List<String> headList = getHeaderData(cellStyleHeader, sheetOne);
/**
* 二、填充第一个sheet数据(第二个sheet类似)
*/
getContentData(sheetOne, headList);
response.setCharacterEncoding("UTF-8");
response.setHeader("content-Type", "application/vnd.ms-excel");
response.setHeader("Content-Disposition", "attachment;filename=" +
URLEncoder.encode("demo" + "." + "xlsx", "UTF-8"));
// 输出
wb.write(response.getOutputStream());
}
/**
* 设置表头数据
*
* @param wb
* @return
*/
private CellStyle getCellStyle(SXSSFWorkbook wb) {
CellStyle cellStyleHeader = wb.createCellStyle();
// 标题水平居中
cellStyleHeader.setAlignment(HorizontalAlignment.CENTER);
// 标题垂直居中
cellStyleHeader.setVerticalAlignment(VerticalAlignment.CENTER);
// 字体加粗
Font font = wb.createFont();
font.setBold(true);
// 字体大小
font.setFontHeight((short) 300);
font.setFontName("宋体");
cellStyleHeader.setFont(font);
return cellStyleHeader;
}
/**
* 一、确定第一个sheet表头(第二个sheet类似)
*/
private List<String> getHeaderData(CellStyle cellStyleHeader, SXSSFSheet sheetOne) {
//确定行:假如第一个sheet有4行(其中有一行是表头行),其余需要从库中查出数据
//确定列:假如第一个sheet有8列,需要从库中查出数据
//假如有3行,8列,其中8是固定的5列加上时间的列数3(当然这里3是动态的,可以是7或者8或者9,这里是演示,所以写为3)
int timeCount = 5;
Row row = sheetOne.createRow(0);
List<String> headList = new ArrayList<>();
//确定首行中的固定列,也就是表头固定列(excel中列是从第0列开始的)
for (int j = 0; j < timeCount; j++) {
Cell cell = row.createCell(j);
//设置表头样式
cell.setCellStyle(cellStyleHeader);
sheetOne.setColumnWidth(j, 25 * 140);
if (j == 0) {
cell.setCellValue("固定列名1");
} else if (j == 1) {
cell.setCellValue("固定列名2");
} else if (j == 2) {
cell.setCellValue("固定列名3");
} else if (j == 3) {
cell.setCellValue("固定列名4");
} else {
cell.setCellValue("固定列名5");
}
}
//确定首行中的动态列,也就是表头动态列,模拟动态月份的表头(假如有3个月)
int trendsCount = 3;
for (int a = 5; a < 5 + trendsCount; a++) {
Cell cell = row.createCell(a);
//设置表头样式
cell.setCellStyle(cellStyleHeader);
//设置每一列的宽度(每列的宽度只需要在首行中设置就行)
sheetOne.setColumnWidth(a, 25 * 140);
String value = "2022-" + a;
cell.setCellValue(value);
//注意此处会在填充数据时会用到
headList.add(value);
}
return headList;
}
/**
* 二、填充第一个sheet数据(第二个sheet类似)
*/
private void getContentData(SXSSFSheet sheetOne, List<String> headList) {
//开始填充数据
for (int rowNum = 1; rowNum < 4; rowNum++) {
Row dataRow = sheetOne.createRow(rowNum);
//1.填充固定列数据(固定列数据可以抽象为一个对象),此处为造的假数据
ExcelTable excelTableOne = new ExcelTable("第一列数据", "第二列数据", "第三列数据", "第四列数据", "第五列数据");
ExcelTable excelTableTwo = new ExcelTable("第一列数据", "第二列数据", "第三列数据", "第四列数据", "第五列数据");
ExcelTable excelTableThree = new ExcelTable("第一列数据", "第二列数据", "第三列数据", "第四列数据", "第五列数据");
List<ExcelTable> list = new ArrayList<>();
list.add(excelTableOne);
list.add(excelTableTwo);
list.add(excelTableThree);
for (int i = 0; i < 5; i++) {
Cell cell = dataRow.createCell(i);
if (i == 0) {
cell.setCellValue(list.get(rowNum - 1).getFixedColOne());
} else if (i == 1) {
cell.setCellValue(list.get(rowNum - 1).getFixedColTwo());
} else if (i == 2) {
cell.setCellValue(list.get(rowNum - 1).getFixedColThree());
} else if (i == 3) {
cell.setCellValue(list.get(rowNum - 1).getFixedColFour());
} else {
cell.setCellValue(list.get(rowNum - 1).getFixedColFive());
}
}
//2.填充动态列数据(注意这里的起始列是固定列的最后一行加1)
for (int i = 5; i < headList.size() + 5; i++) {
Cell cell = dataRow.createCell(i);
//此时需要把这一行的第一个元素:(String fixedColOne = list.get(rowNum).getFixedColOne();)
//和这一列的元素:i = 5....都拿出来作为条件去数据库中查到这行这列确定的单元格的内容,然后进行填充
//查db
// cell.setCellValue("库中查到的数据");
cell.setCellValue("第" + ((i + 1) + "列数据"));
}
}
}
3.3 结果测试
- 1 实现了固定表头+动态表头
- 2 实现了固定表头对应的固定列数据的填充
- 3 实现了动态表头对应的动态列数据的填充
4.优缺分析
优点:可以动态的实现数据的填充,不用拘泥于格式的多样性。因为该方法时针对每一个单元格进行操作的。
缺点:当数据量大的时候会影响导出速度,导致导出速度变慢。题外话:笔者在导出的时候,还特意 调整了网关的响应时长,不然会响应超时,所以各位请慎重选择此方法。或者有大佬能对此进行优化,欢迎指出,不胜感激。