使用 org.apache.poi 与自己封装工具类实现相关功能。代码如下
implementation(group: 'org.apache.poi',name: 'poi-ooxml',version: '4.1.0')
implementation(group: 'org.apache.poi',name: 'poi',version: '4.1.0')
implementation(group: 'cn.hutool', name: 'hutool-all', version: '5.8.3')
import cn.hutool.json.JSONUtil;
import com.longze.fengqx.HeaderNode;
import com.longze.fengqx.PoiModel;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.RegionUtil;
import org.apache.poi.xssf.streaming.SXSSFCell;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* @author Fengqx
* @version 1.0
* @description: excel文件合并
* @date 2023/8/20 13:13
*/
public class ExcelMergeUtil {
public static SXSSFSheet createExcelHead(SXSSFWorkbook book, String sheetName, String headJson){
List headerNodes = JSONUtil.toList(headJson, HeaderNode.class);
SXSSFSheet sxssfSheet = book.createSheet(sheetName);
CellStyle headStyle = book.createCellStyle();
defaultHeadStyle(headStyle);
//表头层级
int deep = headerNodes.stream().map(HeaderNode::getRow).reduce(Integer::max).orElse(1);
for (int i = 0; i < deep; i++) {
sxssfSheet.createRow(i);
}
//创建单元格
for (HeaderNode headerNode : headerNodes) {
int row = headerNode.getRow();
int col = headerNode.getColumn();
SXSSFCell sxssfCell = sxssfSheet.getRow(row).createCell(col);
sxssfSheet.setColumnWidth(col, headerNode.getWidth() * 256);
sxssfCell.setCellStyle(headStyle);
sxssfCell.setCellValue(headerNode.getHeaderName());
CellRangeAddress region;
//是否跨列
if (headerNode.isOverNode()) {
region = new CellRangeAddress(row, deep, col, col);
} else {
region = new CellRangeAddress(row, row, col, (col + headerNode.getOverNodeCount() - 1));
}
if (region.getNumberOfCells() > 1) {
sxssfSheet.addMergedRegionUnsafe(region);
//合并后设置下边框
RegionUtil.setBorderTop(BorderStyle.THIN, region, sxssfSheet);
RegionUtil.setBorderLeft(BorderStyle.THIN, region, sxssfSheet);
RegionUtil.setBorderBottom(BorderStyle.THIN, region, sxssfSheet);
RegionUtil.setBorderRight(BorderStyle.THIN, region, sxssfSheet);
}
}
return sxssfSheet;
}
public static void mergeCellFunc(Sheet sheet, String[] title, String[] field, List
HeaderNode.java 和 PoiModel.java
public class PoiModel {
private String content;
private String oldContent;
private String primaryKey;
private int rowIndex;
private int cellIndex;
public PoiModel() {
}
public PoiModel(String content, String oldContent, String primaryKey, int rowIndex, int cellIndex) {
this.content = content;
this.oldContent = oldContent;
this.primaryKey = primaryKey;
this.rowIndex = rowIndex;
this.cellIndex = cellIndex;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getOldContent() {
return oldContent;
}
public void setOldContent(String oldContent) {
this.oldContent = oldContent;
}
public String getPrimaryKey() {
return primaryKey;
}
public void setPrimaryKey(String primaryKey) {
this.primaryKey = primaryKey;
}
public int getRowIndex() {
return rowIndex;
}
public void setRowIndex(int rowIndex) {
this.rowIndex = rowIndex;
}
public int getCellIndex() {
return cellIndex;
}
public void setCellIndex(int cellIndex) {
this.cellIndex = cellIndex;
}
}
public class HeaderNode {
/**
* 标题头
*/
private String headerName;
/**
* 层级
*/
private int row;
/**
* 非叶子节点列跨度
*/
private int overNodeCount;
/**
* 当前列没有子节点
*/
private boolean overNode = true;
/**
* 列
*/
private int column;
/**
* 宽度
*/
private int width = 13;
public String getHeaderName() {
return headerName;
}
public void setHeaderName(String headerName) {
this.headerName = headerName;
}
public int getRow() {
return row;
}
public void setRow(int row) {
this.row = row;
}
public int getOverNodeCount() {
return overNodeCount;
}
public void setOverNodeCount(int overNodeCount) {
this.overNodeCount = overNodeCount;
}
public boolean isOverNode() {
return overNode;
}
public void setOverNode(boolean overNode) {
this.overNode = overNode;
}
public int getColumn() {
return column;
}
public void setColumn(int column) {
this.column = column;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
}
@GetMapping(value = "/downExcel")
@ResponseBody
public void downExcel(HttpServletResponse response,@RequestParam(required = true) String type) throws Exception {
try {
tengxunService.downExcel(response, type);
} catch (Exception ex) {
throw ex;
}
}
@Override
public void downloadExcel(HttpServletResponse response, String type)throws Exception {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码
String fileName = URLEncoder.encode("腾讯充值文件", "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
OutputStream os = response.getOutputStream();
//加工数据
List> list = Lists.newArrayList();
for(int i=0;i mergeIndex = Arrays.asList(0,1,7);
//6、合并
ExcelMerge.mergeCellFunc(sxssfSheet,title,field, list, sxssfSheet.getLastRowNum(),mergeIndex);
//创建excel文件 下载
book.write(os);
} catch (IOException e){
logger.error("文件导出失败,错误信息{}",e);
// 重置response
response.reset();
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
Map map = new HashMap<>();
map.put("statusCode", "500");
map.put("message", "下载文件失败" + e.getMessage());
response.getWriter().println(JSON.toJSONString(map));
}finally {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
完事通过controller调用下载接口,直接可以下载出文件
可以任意改表头,与选择是否要合并的字段,当做参数传入,将需要合并的列顺序传入即可完成合并,一步到位,十分方便
//5、需要合并的列 ListmergeIndex = Arrays.asList(0,1,7); ExcelMerge.mergeCellFunc(sxssfSheet,title,field, list, sxssfSheet.getLastRowNum(),mergeIndex);
截图如下
荆轲刺秦王!