一。情景描述
最近在项目中客户提到一个新的需求,一开始是在列表查询时导出多个Excel表格,后面提到将多个Excel表格进行合并,实现一个sheet显示多个sheet内容,图示如下:
一开始:
合并后(不同表格空一行隔开):
二。实现思路
首先,先按照生成五张表的思路来生成创建一个Workbook sourceWorkbook
,然后再创建一个Workbook targetWorkbook
,创建一个新的Sheet targetSheet
工作表,之后将sourceWorkbook
中第一个Sheet sheet1
中的内容复制到该表中,再将第二个Sheet sheet2
中的内容复制到targetSheet
中,依次操作,复制完sourceWorkbook
中全部的五张表,即可实现将多个Sheet合并为一个Sheet的操作。
三。示例代码
1.POIUtil工具类
package com.cdtye.itps.jjxt.model.util;
import com.cdtye.itps.jjxt.model.excel.CellRangeAddressExcelVo;
import org.apache.poi.ss.usermodel.*;
import org.springframework.util.CollectionUtils;
import java.util.List;
/**
* @Author Zhongks
* @Description //TODO POI导出excel工具类
* @Date 17:16 2021/5/11
* @Param
* @return
**/
public class POIUtil {
/**
* @Author Zhongks
* @Description //TODO 拷贝sheet(表)
* @Date 17:16 2021/5/11
* @Param [targetSheet, sourceSheet, targetWork, sourceWork, startRow, cellRangeAddressExcelVoList]
* @return void
**/
public static void copySheet(Sheet targetSheet, Sheet sourceSheet,
Workbook targetWork, Workbook sourceWork, int startRow,
List<CellRangeAddressExcelVo> cellRangeAddressExcelVoList) {
if(targetSheet == null || sourceSheet == null || targetWork == null || sourceWork == null){
throw new IllegalArgumentException("调用PoiUtil.copySheet()方法时,targetSheet、sourceSheet、targetWork、sourceWork都不能为空,故抛出该异常!");
}
//设置单元格默认宽度
targetSheet.setDefaultColumnWidth(25);
//复制源表中的行
for (int i = sourceSheet.getFirstRowNum(); i <= sourceSheet.getLastRowNum(); i++) {
Row sourceRow = sourceSheet.getRow(i);
Row targetRow = targetSheet.createRow(i+startRow); //创建新的row
if (sourceRow != null) {
copyRow(targetRow, sourceRow,
targetWork, sourceWork);
}
}
//自定义合并单元格样式(若不需要进行单元格合并操作,将cellRangeAddressExcelVoList赋值为null即可)
if(!CollectionUtils.isEmpty(cellRangeAddressExcelVoList)){
//合并单元格
for(CellRangeAddressExcelVo model:cellRangeAddressExcelVoList){
targetSheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(model.getFirstRow(),model.getLastRow(),model.getFirstCol(),model.getLastCol()));
}
}
}
/**
* @Author Zhongks
* @Description //TODO 拷贝row(行)
* @Date 17:17 2021/5/11
* @Param [targetRow, sourceRow, targetWork, sourceWork]
* @return void
**/
public static void copyRow(Row targetRow, Row sourceRow,
Workbook targetWork, Workbook sourceWork) {
if(targetRow == null || sourceRow == null || targetWork == null || sourceWork == null ){
throw new IllegalArgumentException("调用PoiUtil.copyRow()方法时,targetRow、sourceRow、targetWork、sourceWork、targetPatriarch都不能为空,故抛出该异常!");
}
//设置行高
targetRow.setHeight(sourceRow.getHeight());
for (int i = sourceRow.getFirstCellNum(); i < sourceRow.getLastCellNum(); i++) {
Cell sourceCell = sourceRow.getCell(i);
Cell targetCell = null;
if (sourceCell != null && sourceCell.getStringCellValue()!="") {
if (targetCell == null) {
targetCell = targetRow.createCell(i);
}
//拷贝单元格,包括内容和样式
copyCell(targetCell, sourceCell, targetWork, sourceWork);
}
}
}
/**
* @Author Zhongks
* @Description //TODO 拷贝cell(单元格)
* @Date 17:18 2021/5/11
* @Param [targetCell, sourceCell, targetWork, sourceWork]
* @return void
**/
public static void copyCell(Cell targetCell, Cell sourceCell, Workbook targetWork, Workbook sourceWork) {
if(targetCell == null || sourceCell == null || targetWork == null || sourceWork == null ){
throw new IllegalArgumentException("调用PoiUtil.copyCell()方法时,targetCell、sourceCell、targetWork、sourceWork都不能为空,故抛出该异常!");
}
CellStyle targetCellStyle=targetWork.createCellStyle();
targetCellStyle.cloneStyleFrom(sourceCell.getCellStyle());//拷贝样式
//重新添加样式(这里可以根据你的需要重新进行单元格样式添加)
/*targetCellStyle.setBorderTop(BorderStyle.THIN);//设置上边框线
targetCellStyle.setBorderLeft(BorderStyle.THIN);//设置左边框线
targetCellStyle.setBorderBottom(BorderStyle.THIN);//设置下边框线
targetCellStyle.setBorderRight(BorderStyle.THIN);//设置右边框线*/
targetCell.setCellStyle(targetCellStyle);
targetCell.setCellValue(sourceCell.getStringCellValue());
}
}
2.需要合并的单元格位置信息实体
package com.cdtye.itps.jjxt.model.excel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* @ClassName CellRangeAddressExcelVo
* @Description TODO 需要合并的单元格位置信息Vo
* @Author Zhongks
* @Date 2021/5/11 14:09
* @Version 1.0
**/
@Data
@Accessors(chain = true)
@AllArgsConstructor
public class CellRangeAddressExcelVo {
//起始行号
private int firstRow;
//终止行号
private int lastRow;
//起始列号
private int firstCol;
//终止列号
private int lastCol;
}
该实体类是为了进行合并单元格操作,用来存储需要合并的单元格位置信息:
Service层代码:
*
* @Author Zhongks
* @Description //TODO excel导出
* @Date 12:25 2021/5/7
* @Param [list, response]
* @return void
**/
public void export(BureauDayShiftVo bureauDayShiftVo,HttpServletResponse response) {
try {
// 设置下载的Excel名称,以当前时间为文件后缀,
String dateTime = DateUtil.formatDateString(new Date(), DateUtil.DATE_FORMAT);
String fileName = "供电安全质量日交班表"+dateTime+".xlsx";
// 设置响应输出的头类型
response.setHeader("content-Type", "application/vnd.ms-excel");
response.setHeader("Content-Disposition", "attachment;filename="+fileName);
// excel信息部分
//供电处重点信息追踪表信息
bureauDayShiftVo.setTrackFlag(1);
Map<String, Object> trackSafeQualityMap =this.getTrackSafeQualityMap(bureauDayShiftVo);
//日安全质量信息表信息
bureauDayShiftVo.setTrackFlag(0);
Map<String, Object> safeQualityParamsMap =this.getTrackSafeQualityMap(bureauDayShiftVo);
//天窗兑现统计表
Map<String, Object> skylightCashStatisticsMap = this.getSkylightCashStatisticsMap();
//其他安全质量信息表
Map<String, Object> otherSafeQualityInfoMap = this.getOtherSafeQualityInfoMap(bureauDayShiftVo);
//安全质量考核表
Map<String, Object> safeQualityAssessmentMap = this.getSafeQualityAssessmentMap();
//添加表
List<Map<String, Object>> sheetsList = new ArrayList<>();
sheetsList.add(trackSafeQualityMap);
sheetsList.add(safeQualityParamsMap);
sheetsList.add(skylightCashStatisticsMap);
sheetsList.add(otherSafeQualityInfoMap);
sheetsList.add(safeQualityAssessmentMap);
List<Map<String, Object>> sourceSheetsList = new ArrayList<>();
//创建excel文件的方法
Workbook sourceWorkbook = ExcelExportUtil.exportExcel(sheetsList, ExcelType.HSSF);
Workbook targetWorkbook = ExcelExportUtil.exportExcel(sourceSheetsList, ExcelType.HSSF);
Workbook workbook = this.mergeWorkSheet(targetWorkbook, sourceWorkbook);
//通过response输出流直接输入给客户端
ServletOutputStream outputStream = response.getOutputStream();
workbook.write(outputStream);
outputStream.flush();
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @Author Zhongks
* @Description //TODO 返回重点追踪以及非重点追踪excel信息
* @Date 9:31 2021/5/8
* @Param [bureauDayShiftVo]
* @return java.util.List<java.util.Map<java.lang.String,java.lang.Object>>
**/
public Map<String, Object> getTrackSafeQualityMap(BureauDayShiftVo bureauDayShiftVo){
List<BureauDayShiftExcelVo> exportList = new LinkedList<>();
List<Map<String, Object>> allTrackSafeQualityList = this.getAllTrackSafeQualityList(bureauDayShiftVo);
//封装数据
allTrackSafeQualityList.forEach(map -> {
String basicInformation="单位:"+map.get("unitDeptName")+"\n"+
"线别:"+map.get("lineName")+"\n"+
"所亭:"+map.get("bdsSubstationName")+"\n"+
"开关号:"+map.get("switchNo")+"\n"+
"故障地点:"+map.get("faultPlace")+"\n"+
"发生时间:"+DateUtil.formatDateString(map.get("stopDate"), DateUtil.DATE_FORMAT)+"\n"+
"停时(分钟):"+map.get("stopMinute")+"\n"+
"天气:"+map.get("weatherInfo")+"\n"+
"专业分类:"+map.get("faultMajorName")+"\n";
String segmentAnalysis="单位:"+map.get("unitDeptName")+"\n"+
"单位:详见分析报告"+"\n";
String isTrack="";
if(bureauDayShiftVo.getTrackFlag()==0){
isTrack="否";
}else{
isTrack="是";
}
String review="科室:"+map.get("trackUnitDeptName")+"\n"+
"问题类别:"+map.get("faultCategoryConfigName")+"\n"+
"定责考核:"+map.get("dutyType")+"\n"+
"审核结果:"+map.get("switchNo")+"\n"+
"重点追踪:"+isTrack+"\n";
BureauDayShiftExcelVo bureauDayShiftExcelVo =new BureauDayShiftExcelVo(
DateUtil.formatDateString(map.get("inputDate"), DateUtil.DATE_FORMAT),
basicInformation,
(String)map.get("faultDescription"),
(String)map.get("reportType"),
segmentAnalysis,
review,
map.get("safeQualityState").toString(),
String.valueOf(bureauDayShiftVo.getTrackFlag()));
exportList.add(bureauDayShiftExcelVo);
});
ExportParams exportParams = new ExportParams();
//设置边框样式
// exportParams.setStyle(ExcelStyleType.BORDER.getClazz());
// 设置sheet的名称
if(bureauDayShiftVo.getTrackFlag()==0){
exportParams.setSheetName("日安全质量信息");
}else{
exportParams.setSheetName("供电处重点追踪信息");
}
Map<String, Object> map = new HashMap<>();
// title的参数为ExportParams类型,目前仅仅在ExportParams中设置了sheetName
map.put("title", exportParams);
// 模版导出对应得实体类型,即包含了List的对象
map.put("entity", BureauDayShiftExcelVo.class);
// sheet中要填充得数据
map.put("data", exportList);
return map;
}
/**
* @Author Zhongks
* @Description //TODO 返回天窗兑现统计excel信息
* @Date 10:59 2021/5/8
* @Param []
* @return java.util.Map<java.lang.String,java.lang.Object>
**/
public Map<String, Object> getSkylightCashStatisticsMap(){
List<BureauSkylightCashStatisticsExcelVo> exportList = new LinkedList<>();
//ToDo 得到天窗兑现统计列表数据并进行封装
//示例数据
BureauSkylightCashStatisticsCommonExcelVo applicationExcelVo=new BureauSkylightCashStatisticsCommonExcelVo("申请供电类","申请非供电类");
BureauSkylightCashStatisticsCommonExcelVo applicationTimeExcelVo=new BureauSkylightCashStatisticsCommonExcelVo("申请时间供电类","申请时间非供电类");
BureauSkylightCashStatisticsCommonExcelVo getTimeExcelVo=new BureauSkylightCashStatisticsCommonExcelVo("给点时间供电类","给点时间非供电类");
BureauSkylightCashStatisticsCommonExcelVo workTimeExcelVo=new BureauSkylightCashStatisticsCommonExcelVo(null,null);
BureauSkylightCashStatisticsExcelVo bureauSkylightCashStatisticsExcelVo =new BureauSkylightCashStatisticsExcelVo("怀化供电段","高铁","沪昆高速线",
applicationExcelVo,"取消","10","10",applicationTimeExcelVo,getTimeExcelVo,workTimeExcelVo,"天窗取消原因");
exportList.add(bureauSkylightCashStatisticsExcelVo);
exportList.add(bureauSkylightCashStatisticsExcelVo);
exportList.add(bureauSkylightCashStatisticsExcelVo);
//供电处重点追踪信息表
ExportParams exportParams = new ExportParams();
//设置边框样式
// exportParams.setStyle(ExcelStyleType.BORDER.getClazz());
// 设置sheet的名称
exportParams.setSheetName("天窗兑现统计");
Map<String, Object> map = new HashMap<>();
// title的参数为ExportParams类型,目前仅仅在ExportParams中设置了sheetName
map.put("title", exportParams);
// 模版导出对应得实体类型,即包含了List的对象
map.put("entity", BureauSkylightCashStatisticsExcelVo.class);
// sheet中要填充得数据
map.put("data", exportList);
return map;
}
/**
* @Author Zhongks
* @Description //TODO 返回其他安全信息excel信息
* @Date 11:01 2021/5/8
* @Param []
* @return java.util.Map<java.lang.String,java.lang.Object>
**/
public Map<String, Object> getOtherSafeQualityInfoMap(BureauDayShiftVo bureauDayShiftVo){
List<BureauOtherSafeQualityInfoExcelVo> exportList = new LinkedList<>();
//ToDo 得到其他安全信息列表数据并进行封装
BureauSafeQualityOtherInfoVo bureauSafeQualityOtherInfoVo=new BureauSafeQualityOtherInfoVo();
bureauSafeQualityOtherInfoVo.setStartDate(bureauDayShiftVo.getStartDate());
bureauSafeQualityOtherInfoVo.setEndDate(bureauDayShiftVo.getEndDate());
List<Map<String, Object>> list = bureauSafeQualityOtherInfoService.findList(bureauSafeQualityOtherInfoVo);
list.forEach(map->{
BureauOtherSafeQualityInfoExcelVo otherSafeQualityInfoExcelVo=new BureauOtherSafeQualityInfoExcelVo(
DateUtil.formatDateString(map.get("createDatetime"), DateUtil.DATE_FORMAT),
(String)map.get("description"),
(String)map.get("inputStaffName"),
DateUtil.formatDateString(map.get("createDatetime"), DateUtil.DATE_FORMAT),
(String)map.get("modifyStaffName"),
DateUtil.formatDateString(map.get("updateDatetime"), DateUtil.DATE_FORMAT)
);
exportList.add(otherSafeQualityInfoExcelVo);
});
//供电处重点追踪信息表
ExportParams exportParams = new ExportParams();
//设置边框样式
// exportParams.setStyle(ExcelStyleType.BORDER.getClazz());
// 设置sheet的名称
exportParams.setSheetName("其他安全信息");
Map<String, Object> map = new HashMap<>();
// title的参数为ExportParams类型,目前仅仅在ExportParams中设置了sheetName
map.put("title", exportParams);
// 模版导出对应得实体类型,即包含了List的对象
map.put("entity", BureauOtherSafeQualityInfoExcelVo.class);
// sheet中要填充得数据
map.put("data", exportList);
return map;
}
/**
* @Author Zhongks
* @Description //TODO 返回安全质量考核excel信息
* @Date 11:04 2021/5/8
* @Param []
* @return java.util.Map<java.lang.String,java.lang.Object>
**/
public Map<String, Object> getSafeQualityAssessmentMap(){
List<BureauSafeQualityAssessmentExcelVo> exportList = new LinkedList<>();
//ToDo 得到安全质量考核列表数据并进行封装
//供电处重点追踪信息表
ExportParams exportParams = new ExportParams();
//设置边框样式
// exportParams.setStyle(ExcelStyleType.BORDER.getClazz());
// 设置sheet的名称
exportParams.setSheetName("安全质量考核");
Map<String, Object> map = new HashMap<>();
// title的参数为ExportParams类型,目前仅仅在ExportParams中设置了sheetName
map.put("title", exportParams);
// 模版导出对应得实体类型,即包含了List的对象
map.put("entity", BureauSafeQualityAssessmentExcelVo.class);
// sheet中要填充得数据
map.put("data", exportList);
return map;
}
/**
* @Author Zhongks
* @Description //TODO 合并sheet
* @Date 10:39 2021/5/11
* @Param [targetWorkbook, sourceWorkbook]
* @return org.apache.poi.ss.usermodel.Workbook
**/
public static Workbook mergeWorkSheet(Workbook targetWorkbook, Workbook sourceWorkbook){
try{
//第一个sheet
Sheet firstSourceSheet=sourceWorkbook.getSheetAt(0);
//获得第一个sheet总行数
int firstSourceSheetLen=firstSourceSheet.getPhysicalNumberOfRows();
//获取第几个工作表
Sheet secondSourceSheet= sourceWorkbook.getSheetAt(1);
int secondSourceSheetLen=secondSourceSheet.getPhysicalNumberOfRows();
Sheet thirdSourceSheet=sourceWorkbook.getSheetAt(2);
int thirdSourceSheetLen=thirdSourceSheet.getPhysicalNumberOfRows();
Sheet fourSourceSheet=sourceWorkbook.getSheetAt(3);
int fourSourceSheetLen=fourSourceSheet.getPhysicalNumberOfRows();
Sheet fiveSourceSheet=sourceWorkbook.getSheetAt(4);
//表合并后新表名称
Sheet targetSheet = targetWorkbook.createSheet("安全质量信息日交班表");
//表合并(根据startRow来控制各个表之间的距离,这里为空一行)
POIUtil.copySheet(targetSheet, firstSourceSheet, targetWorkbook, sourceWorkbook,0,null);
POIUtil.copySheet(targetSheet, secondSourceSheet, targetWorkbook, sourceWorkbook,firstSourceSheetLen+1,null);
int thirdSourceSheetColLen=thirdSourceSheet.getRow(0).getPhysicalNumberOfCells();
//得到需要合并单元格的坐标列表,row与col都从0开始计算
List<CellRangeAddressExcelVo> cellRangeAddressExcelVoList = getCellRangeAddressExcelVoList(firstSourceSheetLen+secondSourceSheetLen+2, thirdSourceSheetColLen);
//第三张表需要进行合并单元格操作
POIUtil.copySheet(targetSheet, thirdSourceSheet, targetWorkbook, sourceWorkbook,firstSourceSheetLen+secondSourceSheetLen+2,cellRangeAddressExcelVoList);
POIUtil.copySheet(targetSheet, fourSourceSheet, targetWorkbook, sourceWorkbook,firstSourceSheetLen+secondSourceSheetLen+thirdSourceSheetLen+3,null);
POIUtil.copySheet(targetSheet, fiveSourceSheet, targetWorkbook, sourceWorkbook,firstSourceSheetLen+secondSourceSheetLen+thirdSourceSheetLen+fourSourceSheetLen+4,null);
return targetWorkbook;
}catch (Exception e){
log.error("Workbook合并出错",e);
return null;
}
}
/**
* @Author Zhongks
* @Description //TODO 根据表格场景自定义需要返回合并的单元格位置坐标(注意:row与col都从0开始计算)
* @Date 14:23 2021/5/11
* @Param [row, col]
* @return java.util.List<com.cdtye.itps.jjxt.model.excel.CellRangeAddressExcelVo>
**/
public static List<CellRangeAddressExcelVo> getCellRangeAddressExcelVoList(int row,int col){
//合并单元格坐标位置
List<CellRangeAddressExcelVo> list=new LinkedList<>();
for(int i=0;i<15;i++){
if(i<7){
CellRangeAddressExcelVo cellRangeAddressExcelVo=new CellRangeAddressExcelVo(row,row+1,i,i);
list.add(cellRangeAddressExcelVo);
}else{
CellRangeAddressExcelVo cellRangeAddressExcelVo=new CellRangeAddressExcelVo(row,row,i,i+1);
list.add(cellRangeAddressExcelVo);
i++;
}
}
return list;
}