我的做法是把模板填充后保存到本地,使用填充单元格工具进行处理合并
处理前:
合并后:
使用exportExcelByFillInHeBing方法
java
package org.springblade.modules.api.utils;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
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.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.springblade.core.tool.utils.Func;
import org.springframework.core.io.ClassPathResource;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
public class ExcelMergeUtil implements CellWriteHandler {
private int[] mergeColumnIndex;
private int mergeRowIndex;
public ExcelMergeUtil() {
}
public ExcelMergeUtil(int mergeRowIndex, int[] mergeColumnIndex) {
this.mergeRowIndex = mergeRowIndex;
this.mergeColumnIndex = mergeColumnIndex;
}
@Override
public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {
}
@Override
public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
}
@Override
public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
}
@Override
public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
//当前行
int curRowIndex = cell.getRowIndex();
//当前列
int curColIndex = cell.getColumnIndex();
if (curRowIndex > mergeRowIndex) {
for (int i = 0; i < mergeColumnIndex.length; i++) {
if (curColIndex == mergeColumnIndex[i]) {
mergeWithPrevRow(writeSheetHolder, cell, curRowIndex, curColIndex);
break;
}
}
}
}
/**
* 当前单元格向上合并
*
* @param writeSheetHolder
* @param cell 当前单元格
* @param curRowIndex 当前行
* @param curColIndex 当前列
*/
private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex) {
// 创建DataFormatter
DataFormatter formatter = new DataFormatter();
// 修改这行:使用formatter安全获取值
String curData = formatter.formatCellValue(cell);
Cell preCell = cell.getSheet().getRow(curRowIndex - 1).getCell(curColIndex);
// 修改这行:使用formatter安全获取值
String preData = formatter.formatCellValue(preCell);
// 将当前单元格数据与上一个单元格数据比较
Boolean dataBool = preData.equals(curData);
//此处需要注意:因为我是按照主体名称确定是否需要合并的,所以获取每一行第二列数据和上一行第一列数据进行比较,如果相等合并,getCell里面的值,是主体名称所在列的下标,不能大于需要合并列数组的第一个下标
// 修改这行:使用formatter安全获取值
Boolean bool = formatter.formatCellValue(cell.getRow().getCell(0))
.equals(formatter.formatCellValue(cell.getSheet().getRow(curRowIndex - 1).getCell(0)));
if (dataBool && bool) {
Sheet sheet = writeSheetHolder.getSheet();
List<CellRangeAddress> mergeRegions = sheet.getMergedRegions();
boolean isMerged = false;
for (int i = 0; i < mergeRegions.size() && !isMerged; i++) {
CellRangeAddress cellRangeAddr = mergeRegions.get(i);
// 若上一个单元格已经被合并,则先移出原有的合并单元,再重新添加合并单元
if (cellRangeAddr.isInRange(curRowIndex - 1, curColIndex)) {
sheet.removeMergedRegion(i);
cellRangeAddr.setLastRow(curRowIndex);
sheet.addMergedRegion(cellRangeAddr);
isMerged = true;
}
}
// 若上一个单元格未被合并,则新增合并单元
if (!isMerged) {
CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex - 1, curRowIndex, curColIndex, curColIndex);
sheet.addMergedRegion(cellRangeAddress);
}
}
}
/**
* 头部样式
*
* @return
*/
public static WriteCellStyle getHeadWriteCellStyle() {
// 这里需要设置不关闭流
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
//设置背景颜色
headWriteCellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
//设置头字体
WriteFont headWriteFont = new WriteFont();
//字体大小
headWriteFont.setFontHeightInPoints((short) 13);
//是否加粗
headWriteFont.setBold(true);
headWriteCellStyle.setWriteFont(headWriteFont);
//设置头居中
headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
return headWriteCellStyle;
}
/**
* 内容样式
*
* @return
*/
public static WriteCellStyle getContentWriteCellStyle() {
//内容策略
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
//设置 水平居中
contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
//自动换行
contentWriteCellStyle.setWrapped(true);
//垂直居中
contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
//设置左边框
contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);
//设置右边框
contentWriteCellStyle.setBorderRight(BorderStyle.THIN);
//设置上边框
contentWriteCellStyle.setBorderTop(BorderStyle.THIN);
//设置下边框
contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);
return contentWriteCellStyle;
}
/**
* 合并单元格导出excel工具
*
* @param response 响应头
* @param fileName 文件名称
* @param lsit 需要导出的数据
* @param data 对应的excel导出类
* @param mergeColumeIndex 需要合并的下标
* @param mergeRowIndex 从第几行开始合并
* @throws IOException
*/
public static void exportExcel(HttpServletResponse response, String fileName, List lsit, Class data, int[] mergeColumeIndex, int mergeRowIndex) throws IOException {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
String fileNamePath = URLEncoder.encode(fileName + System.currentTimeMillis(), "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileNamePath + ".xlsx");
// 调用合并单元格工具类,此工具类是根据工程名称相同则合并后面数据
ExcelMergeUtil excelFillCellMergeStrategy = null;
//是否需要合并
if (Func.isNotEmpty(mergeColumeIndex) && mergeRowIndex != -1) {
excelFillCellMergeStrategy = new ExcelMergeUtil(mergeRowIndex, mergeColumeIndex);
}
//头部样式
WriteCellStyle headWriteCellStyle = ExcelMergeUtil.getHeadWriteCellStyle();
//内容样式
WriteCellStyle contentWriteCellStyle = ExcelMergeUtil.getContentWriteCellStyle();
HorizontalCellStyleStrategy horizontalCellStyleStrategy =
new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
EasyExcel.write(response.getOutputStream(), data)
.registerWriteHandler(horizontalCellStyleStrategy)
.registerWriteHandler(excelFillCellMergeStrategy)
//sheet显示的名字
.autoCloseStream(Boolean.TRUE).sheet(fileName)
.doWrite(lsit);
}
/**
* 根据模版导出Excel
*
* @param response 输出流
* @param dataList 数据
* @param templatePath 模版的地址
* @param exportExcelName 导出的名称
* @throws IOException 异常
*/
public static void exportExcelByTemplate(HttpServletResponse response, List dataList, String templatePath, String exportExcelName) throws IOException {
String fileName = URLEncoder.encode(exportExcelName, "UTF-8").replaceAll("\\+", "%20");
response.setHeader("content-Type", "application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
ClassPathResource resource = new ClassPathResource(templatePath);
EasyExcel.write(response.getOutputStream()).withTemplate(resource.getInputStream()).sheet().doFill(dataList);
}
//***************************************excel填充模版用*************************************************
/**
* excel填充模版用
*
* @param response 响应
* @param templateFilePath 模版路径
* @param inputFileName 导出文件名
* @param List 数据列表
* @param map 单个对象
* @param sheetAt 第几个sheet
* @param row 获取第几行的高度
* @param mergeColumeIndex 需要合并的下标
* @param mergeRowIndex 从第几行开始合并
* @param fillInIndex 从第几行开始使用自定义样式
* @param endIndex 结束合并的下标
* @param fontSize 字体大小
* @param endRow 样式结束行
* @throws Exception
*/
public static void exportExcelByFillIn(HttpServletResponse response, String templateFilePath, String inputFileName,
List<?> List, int[] mergeColumeIndex, int mergeRowIndex) throws Exception {
/*String fileNamePath = URLEncoder.encode(inputFileName, "UTF-8").replaceAll("\\+", "%20");
response.setHeader("content-Type", "application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-Disposition", "attachment;filename=" + fileNamePath + ".xlsx");
// 调用合并单元格工具类,此工具类是根据工程名称相同则合并后面数据
ExcelMergeUtil excelFillCellMergeStrategy = null;
//是否需要合并
if (Func.isNotEmpty(mergeColumeIndex) && mergeRowIndex != -1) {
excelFillCellMergeStrategy = new ExcelMergeUtil(mergeRowIndex, mergeColumeIndex);
}
//头部样式
WriteCellStyle headWriteCellStyle = ExcelMergeUtil.getHeadWriteCellStyle();
//内容样式
WriteCellStyle contentWriteCellStyle = ExcelMergeUtil.getContentWriteCellStyle();
HorizontalCellStyleStrategy horizontalCellStyleStrategy =
new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
InputStream is = getInputStream(templateFilePath);
ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).withTemplate(is)
.registerWriteHandler(horizontalCellStyleStrategy)
.registerWriteHandler(excelFillCellMergeStrategy)
.excelType(ExcelTypeEnum.XLSX)
.build();
WriteSheet writeSheet = EasyExcel.writerSheet().build();
// 填充列表数据
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
excelWriter.fill(List, fillConfig, writeSheet);
excelWriter.finish();*/
String fileName = URLEncoder.encode(inputFileName, "UTF-8").replaceAll("\\+", "%20");
response.setHeader("content-Type", "application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
ClassPathResource resource = new ClassPathResource(templateFilePath);
EasyExcel.write(response.getOutputStream()).withTemplate(resource.getInputStream()).sheet().doFill(List);
}
/**
* excel填充模版用
*
* @param response 响应
* @param templateFilePath 模版路径
* @param inputFileName 导出文件名
* @param list 数据列表
* @param brokerColumn 经纪人列(字母表示,如"B")
* @param mergeColumns 需要合并的列(字母表示,如"C,D,E,F,G,H,I,J,K")
* @param startRow 起始行(从0开始)
* @throws Exception
*/
public static void exportExcelByFillInHeBing(HttpServletResponse response, String templateFilePath, String inputFileName, String localPath,
List<?> list, String brokerColumn, String mergeColumns, int startRow, String dependencyRules) throws Exception {
// 如果未指定本地路径,使用默认路径
if (localPath == null || localPath.isEmpty()) {
// 获取用户主目录
String userHome = System.getProperty("user.home");
// 创建默认下载目录
File downloadDir = new File(userHome, "Downloads");
if (!downloadDir.exists()) {
downloadDir.mkdirs();
}
localPath = downloadDir.getAbsolutePath();
}
// 确保路径存在
File dir = new File(localPath);
if (!dir.exists()) {
dir.mkdirs();
}
//保存到本地文件路径
String fileName = localPath + inputFileName + ".xlsx";
// 读取模板文件
ClassPathResource resource = new ClassPathResource(templateFilePath);
// 写入到当前目录
try (FileOutputStream fos = new FileOutputStream(fileName)) {
EasyExcel.write(fos)
.withTemplate(resource.getInputStream())
.sheet()
.doFill(list);
}
System.out.println("文件已保存到: " + new File(fileName).getAbsolutePath());
String absolutePath = new File(fileName).getAbsolutePath();
ExcelMergeByBroker.quickMergeWithDependency(absolutePath, brokerColumn, mergeColumns, startRow, dependencyRules);
response.setHeader("content-Type", "application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-Disposition", "attachment;filename=" + inputFileName + ".xlsx");
// 4. 读取文件并写入响应
File file = new File(absolutePath);
try (InputStream is = new FileInputStream(file);
OutputStream os = response.getOutputStream()) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
}
os.flush();
}
}
/**
* 获取流
*
* @param templateFilePath
* @return
* @throws Exception
*/
public static InputStream getInputStream(String templateFilePath) throws Exception {
ClassPathResource res = new ClassPathResource(templateFilePath);
InputStream is = res.getInputStream();
return is;
}
}
java
//使用
//每日统计
String brokerColumn = "B";
//需要合并的列
String mergeColumns = "A,C,D,E,F,G,H,I,N";
//依赖规则,格式:"H:G,N:G" 表示H列依赖G列,N列依赖G列
String dependencyRules = "H:G,N:G";
//从第三行后开始合并
int startRow = 2;
ExcelMergeUtil.exportExcelByFillInHeBing(response, "templates/数据统计模板.xlsx",
System.nanoTime() + "AgentWater", localPath + "/AgentWater/", dataList, brokerColumn, mergeColumns, startRow, dependencyRules);