一、引言
基于HuTool工具类ExcelWriter合并单元格并且使用 jdk1.8 lambda表达式
二、原始数据模板
三、合并后的数据
按照班级名称、班级分数、小组名称、小组得分、人物名称、人物总分进行单元格合并
合并后效果:
四、导入依赖
pom依赖版本不合适可以换其他版本
导出是项目中最常见的功能,例如考勤记录导出,账单明细导出,订单记录导出等等。导出的工具类有许多种,目前常见的有poi,easypoi,poi…,今天我要说的是基于hutool-poi的导出,hutool-poi是将poi做了封装,简化了大量的代码编写。
使用方式:
maven
在项目的pom.xml中引入
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.5.1</version>
</dependency>
gradle
在项目的build.gradle中引入
compile 'cn.hutool:hutool-all:5.7.3'
五、代码逻辑
1.查找数据库返回数据voList;
2.设置导出表头数据;
3.用lambda表达式获取字段分组数据;
4.遍历数据,设置合并规则;
5.将数据保存在list中;
6.ExcelWriter导出excel文件
六、代码
需实现将人物信息导出到excel文件中并且还要按照班级名称,小组名称,人物名称动态合并单元格,所以先创建人物对象
6.1 人物对象
@Data
@AllArgsConstructor //生成所有参数的构造器
public class Person {
//班级名称
private String className;
//班级分数
private double classScore;
//小组名称
private String groupName;
//小组分数
private double groupScore;
//人物姓名
private String personName;
//人物总分
private double personScore;
//学科名称
private String subjectName;
//学科分数
private double subjectScore;
}
6.2 导出核心代码
@Slf4j
@RestController
@RequestMapping("/person")
public class PersonController {
/**
* 将页面的数据导出并合并单元格
* @param response
*/
@ApiOperation(value = "导出数据到excel")
@ApiImplicitParams({
@ApiImplicitParam(name = "response", value = "响应体对象", dataType = "HttpServletResponse")
})
@GetMapping("/export")
public void export(HttpServletResponse response)throws Exception{
//1.模拟一些人物对象数据(工作中从数据库查出来)
List<Person> list = new ArrayList<>();
list.add(new Person("一班",90,"一组",89,"孙悟空",89,"语文",89));
list.add(new Person("一班",90,"一组",89,"孙悟空",89,"数学",98));
list.add(new Person("一班",90,"一组",89,"唐僧",78,"语文",98));
list.add(new Person("一班",90,"一组",89,"唐僧",78,"数学",78));
list.add(new Person("一班",90,"二组",90,"沙悟净",90,"语文",90));
list.add(new Person("一班",90,"二组",90,"沙悟净",90,"数学",90));
list.add(new Person("二班",91,"一组",97,"鲁智深",98,"语文",89));
list.add(new Person("二班",91,"一组",97,"鲁智深",98,"数学",98));
list.add(new Person("二班",91,"二组",89,"宋江",89,"语文",98));
list.add(new Person("二班",91,"二组",89,"宋江",89,"数学",78));
list.add(new Person("二班",91,"二组",89,"林冲",88,"语文",90));
list.add(new Person("二班",91,"二组",89,"林冲",88,"数学",90));
//2.定义基础数据
List<String> rowHead = CollUtil.newArrayList("班级名称","班级分数","小组名称","小组分数","角色姓名","角色总分","学科名称","学科分数");
//3.通过ExcelUtil.getBigWriter()创建Writer对象,BigExcelWriter用于大数据量的导出,不会引起溢出;
ExcelWriter writer = ExcelUtil.getBigWriter();
//4.写入标题
writer.writeHeadRow(rowHead);
ServletOutputStream out = null;
//5.实现核心逻辑
try {
//6.定义容器保存人物数据
List<List<Object>> rows = new LinkedList<>();
//7.按照班级进行分组
LinkedHashMap<String, List<Person>> classList = list.stream().collect(Collectors.groupingBy(item -> item.getClassName(),
LinkedHashMap::new, Collectors.toList()));
//8.定义起始行(方便分组后合并时从哪一行开始)
//因为标题已经占了一行,所以数据从第二行开始写(excel第一行索引为0)
//因需要合并到人物分数单元格所以需定义如下起始坐标
int indexClassName = 1; //班级名称起始行
int indexClassScore = 1;
int indexGroupName = 1;
int indexGroupScore = 1;
int indexPersonName = 1;
int indexPersonScore = 1;
//9.遍历按班级名分组后的list(用entrySet效率比keySet效率高)
for (Map.Entry<String, List<Person>> classNameListEntry : classList.entrySet()) {
//10.获取按照班级名分组后的集合
List<Person> classValue = classNameListEntry.getValue();
//11.计算此集合的长度
int classSize = classValue.size();
//12.如果只有一行数据不能调用merge方法合并数据,否则会报错
if (classSize == 1){
indexClassName += classSize;
indexClassScore += classSize;
indexGroupName += classSize;
indexGroupScore += classSize;
indexPersonName += classSize;
indexPersonScore += classSize;
}else{
//13.根据班级名称进行合并单元格
//合并行,第一个参数是合并行的开始行号(行号从0开始),第二个参数是合并行的结束行号,第三个参数是合并的列号开始(列号从0开始),
//第四个参数是合并的列号结束,第五个参数是合并后的内容,null不设置,第六个参数指是否支持设置样式,true指的是。
writer.merge(indexClassName, indexClassName + classSize - 1, 0, 0, null, true);
//14.合并完后起始索引移到下一个合并点
indexClassName += classSize;
//15.因为班级分数与班级名称相关联,所以不需要对班级分数分组,直接在此基础上对班级分数合并
writer.merge(indexClassScore, indexClassScore + classSize - 1, 1, 1, null, true);
indexClassScore += classSize;
//16.按照小组名进行分组(以下分组与班级名合并类似)
LinkedHashMap<String, List<Person>> groupList = classValue.stream().collect(Collectors.groupingBy(item -> item.getGroupName(),
LinkedHashMap::new, Collectors.toList()));
for (Map.Entry<String, List<Person>> groupListEntry : groupList.entrySet()) {
List<Person> groupValue = groupListEntry.getValue();
int groupSize = groupValue.size();
if (groupSize == 1){
indexGroupName += groupSize;
indexGroupScore += groupSize;
indexPersonName += groupSize;
indexPersonScore += groupSize;
}else{
//合并小组
writer.merge(indexGroupName, indexGroupName + groupSize - 1, 2, 2, null, true);
indexGroupName += groupSize;
//合并小组分数
writer.merge(indexGroupScore, indexGroupScore + groupSize - 1, 3, 3, null, true);
indexGroupScore += groupSize;
//17.按照人物名称进行分组
LinkedHashMap<String, List<Person>> personList = groupValue.stream().collect(Collectors.groupingBy(item -> item.getPersonName(),
LinkedHashMap::new, Collectors.toList()));
for (Map.Entry<String, List<Person>> personListEntry : personList.entrySet()) {
List<Person> personValue = personListEntry.getValue();
int personSize = personValue.size();
if (personSize == 1){
indexPersonName += personSize;
indexPersonScore += personSize;
}else{
//合并人物
writer.merge(indexPersonName, indexPersonName + personSize - 1, 4, 4, null, true);
indexPersonName += personSize;
//合并人物总分
writer.merge(indexPersonScore, indexPersonScore + personSize - 1, 5, 5, null, true);
indexPersonScore += personSize;
}
}
}
}
}
//18.保存数据
classValue.forEach(
sList->{
List<Object> rowA = null;
rowA = CollUtil.newArrayList(
sList.getClassName(),
sList.getClassScore(),
sList.getGroupName(),
sList.getGroupScore(),
sList.getPersonName(),
sList.getPersonScore(),
sList.getSubjectName(),
sList.getSubjectScore()
);
rows.add(rowA);
}
);
}
//19.导出数据
//logger.info("导出数据:{}",rows.toString());
// 一次性写出内容,使用默认样式,强制输出标题
writer.write(rows, true);
//response为HttpServletResponse对象
response.setContentType("application/vnd.ms-excel;charset=utf-8");
//获取当前日期作为文件名
Date currentDate = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
String date = sdf.format(currentDate);
//test.xls是弹出下载对话框的文件名,不能为中文,中文请自行编码
//response.setHeader("Content-Disposition", "attachment;filename=file.xlsx");
response.setHeader("Content-Disposition", "attachment;filename=report_"+date+".xlsx");
out = response.getOutputStream();
writer.flush(out, true);
}finally {
//关闭输出Servlet流
IoUtil.close(out);
//关闭writer,释放内存
writer.close();
}
}
}