poi导出单元格的同时合并相同内容
周一刚写了个poi打出图片的功能,然后看我刚写完这一个模块,就把其他页面导出表格的也交给我了。
页面需求就是这样
因为刚开始接触poi的我并不熟悉,所以一开始也是百度了很多帖子,最后选了这位大佬的方法。原文链接各位也可以看看这个,比我介绍的更详细。
核心思想就是创建一个工具类,去记录上一行的内容,记录开始合并的行标,列标,插入时和上一行的内容进行比较,不相同则合并,同时更新行标和列标
工具类的代码如下:
import lombok.Data;
@Data
public class PoiModel {
//内容
private String content;
//行标
private int rowIndex;
//列标
private int cellIndex;
//标记
public int flog;
}
导出poi方法的代码块如下
public static XSSFWorkbook createExcelCard(List<?> list, List<String> columnName, List<String> columnData,String fileName) {
/*初始化excel模板*/
XSSFWorkbook workbook = new XSSFWorkbook();
XSSFSheet sheet = workbook.createSheet("123");
//给表头添加行
XSSFRow row = sheet.createRow(0);
for (int i = 0, k = 0; i < columnName.size(); i++, k++) {
sheet.setColumnWidth(k, 4000);
XSSFCell cell = row.createCell(k);
cell.setCellValue(columnName.get(i));
cell.setCellStyle(cellStyle);
}
//填入数据
List<PoiModel> poiModels=new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
XSSFRow nrow = sheet.createRow(i + 1);
Map map = objectToMap(list.get(i));
if (map == null) {
continue;
}
//j控制每一行的格子里填入的数据
for (int j = 0; j < columnData.size(); j++) {
XSSFCell cell = nrow.createCell(j);
String column = columnData.get(j);
//这个if就是做一些数据处理,
if ("typeName".equals(column)) {
cell.setCellValue(getV(map, "typeCode", "typeName"));
} else if ("departmentName".equals(column)) {
String value=getV(map, "departmentCode", "departmentName");
if (""==value){
value="null无用部门";
}
cell.setCellValue(value);
}else if ("agyName".equals(columnData.get(j))){
cell.setCellValue(map.get("agyCode").toString()+map.get("agyName").toString());
} else {
Object value = map.get(columnData.get(j));
if (value == null) {
cell.setCellValue("");
} else if (value instanceof BigDecimal) {
cell.setCellValue(((BigDecimal) value).doubleValue());
} else if (value instanceof String) {
cell.setCellValue((String) value);
} else if (value instanceof Integer) {
cell.setCellValue((Integer) value);
}
}
//j小于2是因为只有前两列需要合并
if (j<2){
//getV方法只是自己写的一个获取数据的处理,只需要把你往表格填的内容赋值给value就可以
String value="";
//获取每列填入的内容,有些特殊的列需要进行处理
if ("departmentCode".equals(columnData.get(j))){
value=getV(map, "departmentCode", "departmentName");
if ("".equals(value)){
value="null无用部门";
}
}else if ("agyName".equals(columnData.get(j))){
value= map.get("agyCode").toString()+map.get("agyName").toString();
}else {
value= (String) map.get(columnData.get(j));
}
//当进行到每列的第一行时,创建辅助类用来记录数据用来判断
if (i==0){
PoiModel poiModel = new PoiModel();
poiModel.setContent(value);
poiModel.setRowIndex(i+1);//行数
poiModel.setCellIndex(j);//列数
poiModels.add(poiModel);
}else{
//第一列时不需要考虑前一列内容不一样时不进行合并的情况,所以当内容不同直接进行合并
if (j==0&& !value.equals(poiModels.get(j).getContent())){
//当合并时CellRangeAddress c1 = new CellRangeAddress();的参数不允许为2 2 0 0这种情况,所以进行判断
if (poiModels.get(j).getRowIndex()!=i){
CellRangeAddress c1 = new CellRangeAddress(poiModels.get(j).getRowIndex() , i,poiModels.get(j).getCellIndex() , poiModels.get(j).getCellIndex());
sheet.addMergedRegion(c1);
}
//每当合并时把第二列的flog修改为1,当进行到第二列时,不管下一行是否相同,直接进行合并。
poiModels.get(j+1).setFlog(1);
poiModels.get(j).setContent(value);
poiModels.get(j).setRowIndex(i+1);
}
//第二列时,当flog为1时,代表第一列合并过了,所以直接合并,并且把辅助类里面的内容进行修改,并把状态改为0
if (j==1&&poiModels.get(j).getFlog()==1){
if (poiModels.get(j).getRowIndex()!=i){
CellRangeAddress c1 = new CellRangeAddress(poiModels.get(j).getRowIndex() , i,poiModels.get(j).getCellIndex() , poiModels.get(j).getCellIndex());
sheet.addMergedRegion(c1);
}
poiModels.get(j).setRowIndex(i+1);
poiModels.get(j).setContent(value);
poiModels.get(j).setFlog(0);
}else if (j==1&&!value.equals(poiModels.get(j).getContent())){
if (poiModels.get(j).getRowIndex()!=i){
CellRangeAddress c1 = new CellRangeAddress(poiModels.get(j).getRowIndex() , i,poiModels.get(j).getCellIndex() , poiModels.get(j).getCellIndex());
sheet.addMergedRegion(c1);
}
poiModels.get(j).setContent(value);
poiModels.get(j).setRowIndex(i+1);
}
//当进行到最后一行时,处理最后一行和上面的行合并
if (i==list.size()-1){
if (poiModels.get(j).getRowIndex()!=i+1){
CellRangeAddress c1 = new CellRangeAddress(poiModels.get(j).getRowIndex() , i+1,poiModels.get(j).getCellIndex() , poiModels.get(j).getCellIndex());
sheet.addMergedRegion(c1);
}
}
}
}
}
}
return workbook;
}
其中list为需要插入的数据集合,columnName为标题行,columnData和标题行对应,为了在map中获取数据,导出方法中用到的一些处理数据的方法
private static Map objectToMap(Object data) {
Map<String, Object> map = new HashMap();
try {
Class obj = data.getClass();
Field[] fields = obj.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
map.put(field.getName(), field.get(data));
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return map;
}
controller中的方法调用如下
public void cardSummary(AstCardQO astCard, HttpServletResponse response, HttpServletRequest request,String flag) {
try {
String fileName = null;
List<String> columnName = new ArrayList<>();
List<String> columnData = new ArrayList<>();
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
response.setContentType("application/octet-stream;charset=UTF-8");
String date = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
//导出卡片表头
fileName = "表名.xlsx";
PageResult<AstCardListVO> cardSum=null;
cardSum = astCardListService.getDepartmentCardSum(astCard);
columnName.addAll(Arrays.asList("标题1", "标题4", "标题3", "标题5"....));
columnData.addAll(Arrays.asList("标题1所对应的字段", "标题2所对应的字段", "标题3所对应的字段", "标题4所对应的字段"...));
list = cardSum.getResult();//需要导入的数据集合
XSSFWorkbook workbook = ExcelUtil.createExcelCard(list, columnName, columnData,fileName);
String userAgent = request.getHeader("user-agent");
Boolean b = userAgent.contains("Edge")
&& (userAgent.contains("Firefox") || userAgent.contains("Chrome") || userAgent.contains("Safari"));
if (b) {
fileName = new String((fileName).getBytes(), "ISO8859-1");
} else {
//其他浏览器
fileName = URLEncoder.encode(fileName, "UTF8");
}
response.addHeader("Content-Disposition", "attachment;filename=" + fileName);
OutputStream os = response.getOutputStream();
workbook.write(os);
os.flush();
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
因为我这个需求只需要合并前两列,如果有多列合并的话,主要是前一列合并之后修改后一列的flog用来判断。