想要达到的效果
引入maven引用
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.2.1</version>
</dependency>
按照要求创建模版
备注 :
模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替
// {} 代表普通变量 {.} 代表是list的变量 {前缀.} 前缀可以区分不同的list
代码部分 :
合并策略代码 :
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.merge.AbstractMergeStrategy;
import io.jsonwebtoken.lang.Collections;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;
import java.util.ArrayList;
import java.util.List;
// 自定义合并策略 该类继承了AbstractMergeStrategy抽象合并策略,需要重写merge()方法
public class CustomMergeStrategy extends AbstractMergeStrategy {
/**
* 分组,每几行合并一次
*/
private List<Integer> exportFieldGroupCountList;
/**
* 目标合并列index
*/
private Integer targetColumnIndex;
// 需要开始合并单元格的首行index
private Integer rowIndex;
// exportDataList为待合并目标列的值
public CustomMergeStrategy(List<String> exportDataList, Integer targetColumnIndex) {
this.exportFieldGroupCountList = getGroupCountList(exportDataList);
this.targetColumnIndex = targetColumnIndex;
}
@Override
protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
if (null == rowIndex) {
rowIndex = cell.getRowIndex();
}
// 仅从首行以及目标列的单元格开始合并,忽略其他
if (cell.getRowIndex() == rowIndex && cell.getColumnIndex() == targetColumnIndex) {
mergeGroupColumn(sheet);
}
}
private void mergeGroupColumn(Sheet sheet) {
int rowCount = rowIndex;
for (Integer count : exportFieldGroupCountList) {
if(count == 1) {
rowCount += count;
continue ;
}
// 合并单元格
CellRangeAddress cellRangeAddress = new CellRangeAddress(rowCount, rowCount + count - 1, targetColumnIndex, targetColumnIndex);
sheet.addMergedRegionUnsafe(cellRangeAddress);
rowCount += count;
}
}
// 该方法将目标列根据值是否相同连续可合并,存储可合并的行数
private List<Integer> getGroupCountList(List<String> exportDataList){
if (Collections.isEmpty(exportDataList)) {
return new ArrayList<>();
}
List<Integer> groupCountList = new ArrayList<>();
int count = 1;
for (int i = 1; i < exportDataList.size(); i++) {
if (exportDataList.get(i).equals(exportDataList.get(i - 1))) {
count++;
} else {
groupCountList.add(count);
count = 1;
}
}
// 处理完最后一条后
groupCountList.add(count);
return groupCountList;
}
}
导出部分 :
@Test
public void complexFillTest() throws IOException {
//正式代码时 改成 input或者 file 传入
String templateFileName = "D:/模版.xls";
//改成导出的output或者其他方式写出 也可
String fileName = "D:/下载的文件.xls";
// 方案1
try (ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).excelType(ExcelTypeEnum.XLS).build()) {
//数据准备 改成自己的即可
InvoiceInfo invoiceInfo = invoiceInfoService.lambdaQuery()
.eq(InvoiceInfo::getId, 21)
.one();
List<FreightOrderResponse> freightOrderResponseList = new ArrayList<>();
ArrayList<Map<String, String>> dataList = new ArrayList<>();
for (int i = 0; i < freightOrderResponseList.size(); i++) {
FreightOrderResponse freightOrderResponse = freightOrderResponseList.get(0);
Map<String, String> map = new HashMap<>();
map.put("companyName",freightOrderResponse.getComName());
if (freightOrderResponseList.size() / 2 < i){
map.put("companyName",freightOrderResponse.getComName() + "1");
}
map.put("sendAddr",freightOrderResponse.getSendProvince());
map.put("arriveAddr",freightOrderResponse.getReceiveProvince());
map.put("goodsName",freightOrderResponse.getGoodsName());
map.put("practicalFee",freightOrderResponse.getOrderPrice().toString());
dataList.add(map);
}
WriteSheet writeSheet = EasyExcel.writerSheet()
// 合并的单元格 并且将需要合并的那一列数据传入
.registerWriteHandler(new CustomMergeStrategy(dataList.stream().map(map -> map.get("companyName")).collect(Collectors.toList()), 0))
.build();
// 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行,然后下面的数据往后移动。默认 是false,会直接使用下一行,如果没有则创建。
// forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了,所以慎用
// 简单的说 如果你的模板有list,且list不是最后一行,下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存
// 如果数据量大 list不是最后一行 参照下一个
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
excelWriter.fill(dataList, fillConfig, writeSheet);
Map<String, Object> map = MapUtils.newHashMap();
BigDecimal reduce = freightOrderResponseList.stream().map(obj -> obj.getOrderPrice() != null ? obj.getOrderPrice() : BigDecimal.ZERO).reduce(BigDecimal.ZERO, BigDecimal::add);
map.put("practicalFee", reduce);
map.put("customName", invoiceInfo.getCustomName());
map.put("sellerComName", invoiceInfo.getSellerComName());
excelWriter.fill(map, writeSheet);
excelWriter.finish();
}
}