转自:https://www.cnblogs.com/fanerwei222/p/12029411.html
某种偶然的机会遇到了这个插件, 听说很牛X, 我之前也不知道, 不过还是耍了一下子, 遂记录下来.
官方的demo官网都有,传送门 : https://alibaba-easyexcel.github.io/index.html
当然, 官网的demo只是简单的演示, 如果你要实现的表格内容比较复杂, 那么需要自己去定义你的数据类, 并且自己去实现, 遂记录下我实现的一个不简单也不复杂的表格.
首先看实现的效果图: 比较粗糙, 不过一般的操作都有

由于我没有设置样式, 所以看起来还是比较丑的, 不过没关系, 功能实现了, 页面后面再说嘛.
直接上pom配置:
<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.0.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
<scope>provided</scope>
</dependency>
这两个是必须要的, lombok主要是方便, 我用的EasyExcel版本是2.0.5, 应该可以换成其他的也没事.
两个数数据类和一个主类直接贴出来看代码吧.
package com.domaven.orange.easyExcel;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.ContentRowHeight;
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
import lombok.Data;
import java.util.Date;
import java.util.List;
/**
* TODO
* 示范数据实体
* {} 括起来的代表着层级标题
*/
@Data
@ContentRowHeight(20)
@HeadRowHeight(20)
@ColumnWidth(25)
public class DemoData {
@ExcelProperty({"2019年商品房预售许可证发证统计报表", "序号"})
private String order;
@ExcelProperty({"2019年商品房预售许可证发证统计报表", "企业名称"})
private String tenantName;
@ExcelProperty({"2019年商品房预售许可证发证统计报表", "项目名称"})
private String projectName;
@ExcelProperty({"2019年商品房预售许可证发证统计报表", "幢号"})
private Integer houseOrder;
@ExcelProperty({"2019年商品房预售许可证发证统计报表", "层数"})
private Integer flowsNums;
@ExcelProperty({"2019年商品房预售许可证发证统计报表", "规划用途"})
private String regularUse;
@ExcelProperty({"2019年商品房预售许可证发证统计报表", "住宅", "住宅套数"})
private Integer houseNums;
@ExcelProperty({"2019年商品房预售许可证发证统计报表", "住宅", "住宅面积"})
private Integer houseArea;
@ExcelProperty({"2019年商品房预售许可证发证统计报表", "非住宅", "非住宅套数"})
private Integer noHouseNums;
@ExcelProperty({"2019年商品房预售许可证发证统计报表", "非住宅", "非住宅面积"})
private Integer noHouseArea;
@ExcelProperty({"2019年商品房预售许可证发证统计报表", "总建筑面积"})
private Integer totalHouseArea;
@ExcelProperty({"2019年商品房预售许可证发证统计报表", "许可证号"})
private Integer admitNums;
@ExcelProperty({"2019年商品房预售许可证发证统计报表", "发证日期"})
private Date subIdDate;
/**
* 小计
*/
@ExcelIgnore
private List<TotalData> minTotal;
/**
* 总计
*/
@ExcelIgnore
private List<TotalData> allTotal;
}
package com.domaven.orange.easyExcel;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.util.Date;
/**
* TODO
* 统计数据实体
*/
@Data
public class TotalData {
@ExcelIgnore
private String order;
@ExcelProperty({"2019年商品房预售许可证发证统计报表", "企业名称"})
private String tenantName;
@ExcelProperty({"2019年商品房预售许可证发证统计报表", "幢号"})
private Integer houseOrder;
@ExcelProperty({"2019年商品房预售许可证发证统计报表", "层数"})
private Integer flowsNums;
@ExcelProperty({"2019年商品房预售许可证发证统计报表", "规划用途"})
private String regularUse;
@ExcelProperty({"2019年商品房预售许可证发证统计报表", "住宅", "住宅套数"})
private Integer houseNums;
@ExcelProperty({"2019年商品房预售许可证发证统计报表", "住宅", "住宅面积"})
private Integer houseArea;
@ExcelProperty({"2019年商品房预售许可证发证统计报表", "非住宅", "非住宅套数"})
private Integer noHouseNums;
@ExcelProperty({"2019年商品房预售许可证发证统计报表", "非住宅", "非住宅面积"})
private Integer noHouseArea;
@ExcelProperty({"2019年商品房预售许可证发证统计报表", "总建筑面积"})
private Integer totalHouseArea;
@ExcelProperty({"2019年商品房预售许可证发证统计报表", "许可证号"})
private Integer admitNums;
@ExcelProperty({"2019年商品房预售许可证发证统计报表", "发证日期"})
private Date subIdDate;
public TotalData setTenantName(String tenantName) {
this.tenantName = tenantName;
return this;
}
public TotalData setOrder(String order) {
this.order = order;
return this;
}
}
package com.domaven.orange.easyExcel;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.merge.LoopMergeStrategy;
import com.alibaba.excel.write.merge.OnceAbsoluteMergeStrategy;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.springframework.beans.BeanUtils;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* TODO
* 使用EasyExcel按照自己想要的数据格式生成excel
*/
public class MainEasyExcel {
/**
* 分批写入数据
* @param args
*/
public static void main(String[] args) {
/**
* 文件保存地址
*/
String fileName = "d://tmp/good.xlsx";
/**
* 弄一个写建造器
*/
ExcelWriterBuilder builder = EasyExcel.write(fileName, DemoData.class);
/**
* 装点数据
*/
List<DemoData> list = data();
/**
* 将数据按企业名称进行分组
*/
Map<String, List<DemoData>> map = list.stream().collect(Collectors.groupingBy(DemoData::getTenantName));
/**
* 给除了"小计""总计"等行外的所有有效数据标序号
*/
final int[] mapOrder = {1};
map.values().forEach(
value ->
value.forEach(
v -> {
v.setOrder(String.valueOf(mapOrder[0]));
mapOrder[0]++;
}
)
);
List<TotalData> allTotalDataList = new ArrayList<>();
/**
* 是否处理总计行标记
*/
boolean isDepressAllTotal = false;
/**
* 从第四行开始合并(下标为3)
*/
int mergeStartWithRow = 3;
/**
* 从第二列开始合并(下标为1)
*/
int mergeStartWithColumn = 1;
/**
* 合并单元格需要用到的参数分别如下
* firstRowIndex : 起始行索引
* lastRowIndex : 结束行索引
* firstColumnIndex : 起始列索引
* lastColumnIndex : 结束列索引
*/
int firstRowIndex = 0, lastRowIndex = 0, firstColumnIndex = 0, lastColumnIndex = 0;
/**
* 填充数据数量计数器
*/
int count = 0;
for (Map.Entry entry : map.entrySet()) {
/**
* 设置要合并的单元格的位置
*/
firstRowIndex = mergeStartWithRow + count;
lastRowIndex = ((List<DemoData>) entry.getValue()).size() + firstRowIndex - 1;
firstColumnIndex = mergeStartWithColumn;
lastColumnIndex = mergeStartWithColumn;
/**
* 合并公司名称行
*/
builder.registerWriteHandler(getOnceMerge(firstRowIndex, lastRowIndex, firstColumnIndex, lastColumnIndex));
/**
* 合并小计行
*/
builder.registerWriteHandler(getOnceMerge(lastRowIndex + 1, lastRowIndex + 1, 0, 1));
count = count + ((List<DemoData>) entry.getValue()).size() + 1;
}
/**
* 合并总计行
* 加1是小计那一行
* 加2是总计那一行
*/
builder.registerWriteHandler(getOnceMerge(lastRowIndex + 2, lastRowIndex + 2, 0, 1));
/**
* 写器
*/
ExcelWriter writer = builder.build();
/**
* sheet页
*/
WriteSheet sheet = EasyExcel.writerSheet("测试").build();
for (Map.Entry entry : map.entrySet()) {
/**
* 分批写入数据
*/
writer.write((List<DemoData>) entry.getValue(), sheet);
/**
* 写入小计行数据
*/
List<TotalData> totalDataList = new ArrayList<>();
totalDataList.add(((List<DemoData>) (entry.getValue())).get(0).getMinTotal().get(0).setTenantName("小计").setOrder("小计"));
writer.write(totalDataList, sheet);
/**
* 设置总计行数据
*/
if (!isDepressAllTotal) {
allTotalDataList.add(((List<DemoData>) (entry.getValue())).get(0).getAllTotal().get(0).setTenantName("总计").setOrder("总计"));
isDepressAllTotal = true;
}
}
/**
* 写入总计行数据
*/
writer.write(allTotalDataList, sheet);
writer.finish();
}
/**
* 测试方法, 直接写入方式
* @param args
*/
public static void mainDirect(String[] args) {
String fileName = "d://tmp/good.xlsx";
List<DemoData> list = data();
Map<String, List<DemoData>> map = list.stream().collect(Collectors.groupingBy(DemoData::getTenantName));
EasyExcel.write(fileName, DemoData.class).sheet("数据模板").doWrite(data());
}
/**
* 按照需要的数据格式构建数据
* @return
*/
private static List<DemoData> data() {
List<DemoData> list = new ArrayList<DemoData>();
for (int i = 0; i < 10; i++) {
DemoData data = new DemoData();
data.setOrder(String.valueOf(i));
if (i % 2 == 0) {
data.setTenantName("北京科技");
} else if (i % 3 == 0) {
data.setTenantName("南京公司");
} else {
data.setTenantName("天津包子铺");
}
data.setProjectName("打铁项目");
data.setHouseOrder(123);
data.setFlowsNums(8);
data.setRegularUse("没用");
data.setHouseNums(2);
data.setHouseArea(120);
data.setNoHouseNums(4);
data.setNoHouseArea(400);
data.setTotalHouseArea(900);
data.setAdmitNums(8888888);
data.setAdmitNums(10);
data.setSubIdDate(new Date());
/**
* 特殊的小计和总计数据
*/
List<TotalData> totalDataList = new ArrayList<>();
TotalData totalData = new TotalData();
BeanUtils.copyProperties(data, totalData);
totalDataList.add(totalData);
data.setMinTotal(totalDataList);
data.setAllTotal(totalDataList);
list.add(data);
}
return list;
}
/**
* 获取样式策略
* 阿里官方模板
* @return
*/
public static HorizontalCellStyleStrategy getStyle() {
/**
* 头的策略
*/
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
/**
* 背景设置为红色
*/
headWriteCellStyle.setFillForegroundColor(IndexedColors.RED.getIndex());
WriteFont headWriteFont = new WriteFont();
headWriteFont.setFontHeightInPoints((short) 20);
headWriteCellStyle.setWriteFont(headWriteFont);
/**
* 内容的策略
*/
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
/**
* 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定
*/
contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
/**
* 背景绿色
*/
contentWriteCellStyle.setFillForegroundColor(IndexedColors.GREEN.getIndex());
WriteFont contentWriteFont = new WriteFont();
/**
* 字体大小
*/
contentWriteFont.setFontHeightInPoints((short) 20);
contentWriteCellStyle.setWriteFont(contentWriteFont);
/**
* 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现
*/
HorizontalCellStyleStrategy horizontalCellStyleStrategy =
new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
return horizontalCellStyleStrategy;
/**
* 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
* EasyExcel.write(fileName, DemoData.class).registerWriteHandler(horizontalCellStyleStrategy).sheet("模板").doWrite(data());
*/
}
/**
* 获取合并策略
* 固定的合并策略,官方模板
* @return
*/
public static LoopMergeStrategy getLoopMergeStrategy() {
/**
* 每隔2行会合并 把eachColumn 设置成 3 也就是我们数据的长度,所以就第一列会合并。当然其他合并策略也可以自己写
*/
LoopMergeStrategy loopMergeStrategy = new LoopMergeStrategy(2, 1);
return loopMergeStrategy;
}
/**
* 合并单元格
* 根据需要指定位置的单元格进行合并
* @param firstRowIndex 起始行索引
* @param lastRowIndex 结束行索引
* @param firstColumnIndex 起始列索引
* @param lastColumnIndex 结束列索引
* @return
*/
public static OnceAbsoluteMergeStrategy getOnceMerge(int firstRowIndex, int lastRowIndex, int firstColumnIndex, int lastColumnIndex) {
OnceAbsoluteMergeStrategy once = new OnceAbsoluteMergeStrategy(firstRowIndex, lastRowIndex, firstColumnIndex, lastColumnIndex);
return once;
}
}
代码注释也是十分详尽的, 可以直接看代码了.
