概要

使用POI导出Excel,需合并单元格

预期

一、简单合并单元格

二、tree结构合并单元格

代码


/*** @ClassName MergeModel* @Description    用于保存需要合并的单元格* @Author * @Create 2023/6/1 14:55* @Modifier* @ModifyTime*/
public class MergeModel {private String content;//内容private int rowIndex;//记录相同内容的开始行号private int cellIndex;//列号public String getContent() {return content;}public void setContent(String content) {this.content = content;}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;}}

工具类:

/*** FileName: ExcelMergeUtil* Author:   Administrator* Date:     2023/6/1 14:49* Description: 复杂excel导出工具类* History:* <author>          <time>          <version>          <desc>* 作者姓名           修改时间           版本号              描述*/import com.fasterxml.jackson.annotation.JsonFormat;
import com.gwos.common.utils.ExceptionUtil;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.*;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.text.DecimalFormat;
import java.util.*;
import java.util.regex.Pattern;public class ExcelMergeUtil {/*** 创建excel文件** @param objData    数据* @param fileName   文件名* @param sheetName  sheet名* @param columns    表头* @param mergeIndex 需要合并的列号集合* @return*/public static int exportToExcelForXlsx(List<List<Object>> objData, String fileName, String sheetName, List<String> columns, List<Integer> mergeIndex, boolean isTree, HttpServletRequest request, HttpServletResponse response) {int flag = 0;Collections.sort(mergeIndex);//将列号排序// 创建工作薄XSSFWorkbook wb = new XSSFWorkbook();// sheet1XSSFSheet sheet1 = wb.createSheet(sheetName);//设置样式XSSFCellStyle style = wb.createCellStyle();style.setAlignment(HorizontalAlignment.CENTER); // 设置水平对齐方式为居中对齐style.setVerticalAlignment(VerticalAlignment.CENTER); // 设置垂直居中对齐//标题头sheet1.createFreezePane(0, 1);//冻结表头XSSFRow sheet1row1 = sheet1.createRow((short) 0);sheet1row1.setHeight((short) 480);XSSFCell title = sheet1row1.createCell(0);title.setCellValue("设备***数据统计"); // 设置第一个单元格的内容XSSFCellStyle titleStyle = wb.createCellStyle();titleStyle.setAlignment(HorizontalAlignment.CENTER); // 设置水平对齐方式为居中对齐titleStyle.setVerticalAlignment(VerticalAlignment.CENTER); // 设置垂直居中对齐XSSFFont cellFont = wb.createFont();cellFont.setFontHeightInPoints((short)18);cellFont.setBold(true);titleStyle.setFont(cellFont);title.setCellStyle(titleStyle);// 合并 A1 到 F1 这 6 个单元格CellRangeAddress region = new CellRangeAddress(0, 0, 0, 6);sheet1.addMergedRegion(region);//表头sheet1.createFreezePane(0, 1);//冻结表头XSSFRow sheet1row2 = sheet1.createRow((short) 1);sheet1row1.setHeight((short) 480);//写入表头if (columns != null && columns.size() > 0) {for (int i = 0; i < columns.size(); i++) {String column = columns.get(i);//列XSSFCell cell2 = sheet1row2.createCell(i);cell2.setCellValue(column);cell2.setCellStyle(style);}}int dataSatrtIndex = 1;//数据开始行boolean isMerge = false;if (mergeIndex != null && mergeIndex.size() != 0) {isMerge = true;}//写入数据if (objData != null && objData.size() > 0) {Map<Integer, MergeModel> poiModels = new HashMap<Integer, MergeModel>();//循环写入表中数据int i = 0;for (; i < objData.size(); i++) {//数据行XSSFRow row = sheet1.createRow((short) (i + dataSatrtIndex));//行内循环,既单元格(列)List<Object> list = objData.get(i);DecimalFormat decimalFormat = new DecimalFormat("0.00");int j = 0;for (Object o : list) {//数据列String content = "";if (o != null) {if (o.toString().contains(".") && isNumeric(o.toString())) {content = decimalFormat.format(Float.valueOf(o.toString()));} else if (o.toString().contains("-") && o.toString().contains(":")) {content = String.valueOf(o).split("\\.")[0];} else {content = String.valueOf(o);}}if (isMerge && mergeIndex.contains(j)) {//如果该列需要合并MergeModel poiModel = poiModels.get(j);if (poiModel == null) {poiModel = new MergeModel();poiModel.setContent(content);poiModel.setRowIndex(i + dataSatrtIndex);poiModel.setCellIndex(j);poiModels.put(j, poiModel);} else {if (!poiModel.getContent().equals(content)) {//如果不同了,则将前面的数据合并写入if (isTree) {//此列向后的所有列都进行一次写入合并操作,并清空。//树结构中存在这种情况,a目录和b目录为同级目录,a目录下最后一个子目录和b目录下的第一个子目录名称相同,防止本来不应该合并的单元格被合并addMergedRegionValue(sheet1, poiModels, mergeIndex, i + dataSatrtIndex, poiModel.getCellIndex());} else {XSSFRow lastRow = sheet1.getRow(poiModel.getRowIndex());XSSFCell lastCell = lastRow.createCell(poiModel.getCellIndex());//创建列lastCell.setCellValue(poiModel.getContent());lastCell.setCellStyle(style);
//合并单元格if (poiModel.getRowIndex() != i + dataSatrtIndex - 1) {sheet1.addMergedRegion(new CellRangeAddress(poiModel.getRowIndex(), i + dataSatrtIndex - 1, poiModel.getCellIndex(), poiModel.getCellIndex()));}}
//将新数据存入poiModel.setContent(content);poiModel.setRowIndex(i + dataSatrtIndex);poiModel.setCellIndex(j);poiModels.put(j, poiModel);}}row.createCell(j);//创建单元格} else {//该列不需要合并//数据列XSSFCell cell = row.createCell(j);cell.setCellValue(content);cell.setCellStyle(style);}j++;}}//将最后一份存入if (poiModels != null && poiModels.size() != 0) {for (Integer key : poiModels.keySet()) {MergeModel poiModel = poiModels.get(key);XSSFRow lastRow = sheet1.getRow(poiModel.getRowIndex());XSSFCell lastCell = lastRow.getCell(poiModel.getCellIndex());lastCell.setCellValue(poiModel.getContent());lastCell.setCellStyle(style);//合并单元格if (poiModel.getRowIndex() != i + dataSatrtIndex - 1) {sheet1.addMergedRegion(new CellRangeAddress(poiModel.getRowIndex(), i + dataSatrtIndex - 1, poiModel.getCellIndex(), poiModel.getCellIndex()));}}}} else {flag = -1;}//设置固定列宽,poi的列宽设置有点操蛋,大概规律网上有不少版本自行百度//这里大概是143像素for (int i = 0; i < columns.size(); i++) {sheet1.setColumnWidth(i, 4550);}OutputStream os = null;try {// 创建一个普通输出流os = response.getOutputStream();fileName = "file.xls";// 请求浏览器打开下载窗口response.reset();response.setCharacterEncoding("UTF-8");// Content-disposition 告诉浏览器以下载的形式打开
//            String header = request.getHeader("User-Agent").toUpperCase();
//            if (header.contains("MSIE") || header.contains("TRIDENT") || header.contains("EDGE")) {
//                fileName = URLEncoder.encode(fileName, "utf-8");
//                fileName = fileName.replace("+", "%20"); // IE下载文件名空格变+号问题
//            } else {
//                fileName = new String(fileName.getBytes(), "ISO8859-1");
//            }fileName = new String(fileName.getBytes(), "ISO8859-1");response.setHeader("Content-Disposition", "attachment; filename=" + fileName);// 要保存的文件名response.setContentType("application/octet-stream");
// 直接用数组缓冲输出流输出wb.write(os);} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {try {wb.close();os.close();} catch (IOException e) {e.printStackTrace();}}/*     本地下载方式FileOutputStream out = null;try {out = new FileOutputStream("E:\\" + fileName + ".xlsx");wb.write(out);} catch (Exception ex) {try {out.flush();out.close();}catch (IOException e){flag = 0;e.printStackTrace();}}*/return flag;}/*** 判断是不是数字** @param str* @return*/private static boolean isNumeric(String str) {if (str == null || str.length() == 0) {return false;}Pattern pattern = Pattern.compile("^[-\\+]?[\\d]*$");return pattern.matcher(str).matches();}private static void addMergedRegionValue(XSSFSheet sheet, Map<Integer, MergeModel> poiModels, List<Integer> mergeIndex, int nowRowIndex, int nowCellIndex) {if (poiModels != null && poiModels.size() != 0 && mergeIndex != null && mergeIndex.size() != 0) {for (Integer index : mergeIndex) {if (index >= nowCellIndex) {MergeModel poiModel = poiModels.remove(index);//删除并获取valueif (poiModel != null) {XSSFRow lastRow = sheet.getRow(poiModel.getRowIndex());XSSFCell lastCell = lastRow.createCell(poiModel.getCellIndex());//创建列lastCell.setCellValue(poiModel.getContent());//合并单元格if (poiModel.getRowIndex() != nowRowIndex - 1) {sheet.addMergedRegion(new CellRangeAddress(poiModel.getRowIndex(), nowRowIndex - 1, poiModel.getCellIndex(), poiModel.getCellIndex()));}}}}}}}

调用层:

@GetMapping("/export")public void exportDeviceMeasureValues( HttpServletRequest request, HttpServletResponse response, @ApiParam(value = "项目编号")@RequestParam String projectNo,@ApiParam(value = "项目名称")@RequestParam String projectName,@ApiParam(value = "日期,格式:yyyy-MM-dd")@RequestParam String inspectDate,@ApiParam(value = "设备类别")@RequestParam String deviceCategory) {try {TowerMonitorVO monitorValues = monitorService.getDeviceMeasureValues(projectNo, formatter.parse(inspectDate),deviceCategory);monitorService.exportDeviceMeasureValues(projectName,monitorValues,deviceCategory,request,response);} catch (Exception e) {LOG.error("", e);}}

tree结构导出

   public static void main(String[] args) {List<String> columns = new ArrayList<>();//标头columns.add("目录");columns.add("目录");columns.add("目录");columns.add("内容");String fileName = "文件名字";//文件名字String sheetName = "sheet名字";//sheet名字//内容数据List<List<Object>> exportData = new ArrayList<>();//行内的数据List<Object> rowData = new ArrayList<Object>();rowData.add("一级目录1");rowData.add("二级目录1");rowData.add("三级目录1");rowData.add("内容1");exportData.add(rowData);List<Object> rowData2 = new ArrayList<Object>();rowData2.add("一级目录1");rowData2.add("二级目录1");rowData2.add("三级目录1");rowData2.add("内容2");exportData.add(rowData2);List<Object> rowData3 = new ArrayList<Object>();rowData3.add("一级目录1");rowData3.add("二级目录1");rowData3.add("三级目录2");rowData3.add("内容3");exportData.add(rowData3);List<Object> rowData4 = new ArrayList<Object>();rowData4.add("一级目录1");rowData4.add("二级目录1");rowData4.add("三级目录2");rowData4.add("内容4");exportData.add(rowData4);List<Object> rowData5 = new ArrayList<Object>();rowData5.add("一级目录1");rowData5.add("二级目录2");rowData5.add("三级目录3");rowData5.add("内容5");exportData.add(rowData5);List<Object> rowData6 = new ArrayList<Object>();rowData6.add("一级目录1");rowData6.add("二级目录2");rowData6.add("三级目录3");rowData6.add("内容6");exportData.add(rowData6);//需要合并的列号List<Integer> mergeIndex = new ArrayList<Integer>();mergeIndex.add(0);mergeIndex.add(1);mergeIndex.add(2);int flag = ExcelMergeUtil.exportToExcelForXlsx(exportData, fileName, sheetName, columns, mergeIndex, true, null, null);System.out.println(flag);}

小结

引用原文:

https://www.cnblogs.com/wdk2020/p/13204162.html

使用POI导出excel并合并单元格可Tree相关推荐

  1. java poi导出excel,合并单元格

    java导出excel一般都是2种情况,一种是依赖一个实体类进行导出,或者把数据查询出来当成一个视图,对视图进行创建实体:另一种方式就是通过数据还要计算,然后一块统计,那么就不是很好处理了,我采用的是 ...

  2. poi 导出Excel 动态 合并单元格

    public String arrearagePeriodExport(ArrearageParam param) {param.setPageNo(1);param.setPageSize(Inte ...

  3. poi 导出excel 中合并单元格

    参数说明 CellRangeAddress(int firstRow, int lastRow, int firstCol, int lastCol) CellRangeAddress(起始行号,终止 ...

  4. Springboot导出excel,合并单元格示例

    原文链接:Springboot导出excel,合并单元格示例 更多文章,欢迎访问:Java知音,一个专注于技术分享的网站 以下用一个示例来说明springboot如何导出数据到excel. 首先引入M ...

  5. Java 利用hutool工具实现导出excel并合并单元格

    Java 利用hutool工具实现导出excel并合并单元格 controller层调用service,就一个核心方法,没错就下面这个代码就能实现了.前提是项目里面要引用hutool包.把我这个复制到 ...

  6. POI进行Excel的合并单元格数据处理

    POI进行Excel的合并单元格数据处理 近日接到一个要处理合并Excel单元格的上料表的需求,就到网上找了一些模板,发现有的技术大牛还是挺厉害的,对他们致以敬意. 合并单元格工具类 在这个类中将传入 ...

  7. JAVA导出excel 动态合并单元格

    JAVA excel合并单元格原生poi 合并后的效果 直接上代码 (该方法为如果指定行的单元格里面的值一致则进行合并,直接粘贴使用即可) ** @param sheet* @param colIdx ...

  8. jxls导出excel,合并单元格的两种方式

    1.引入maven依赖: <dependency><groupId>org.projectlombok</groupId><artifactId>lom ...

  9. easyexcel导出excel自定义合并单元格【动态表头和动态数据均可以自由合并】

    网上合并单元格的博客还是很多的,大家自行舍取吧.本文主要讲解固定与不固定的表头和内容如何合并 参考官网 https://easyexcel.opensource.alibaba.com/docs/cu ...

  10. java excel导出 jxl_java使用JXL导出Excel及合并单元格

    jxl是一个韩国人写的java操作excel的工具,在开源世界中,有两套比较有影响的API可供使用,一个是POI,一个是jExcelAPI.其中功能相对POI比较弱一点.但jExcelAPI对中文支持 ...

最新文章

  1. DSML:深度子空间相互学习模型(用于癌症亚型预测)
  2. qt on android 桌面鼠标事件,Qt on Android 不能自动创建Qt套件的问题的解决
  3. 在java中finalize_在Java垃圾回收中使用finalize()方法
  4. 增加新分类daily——“每天学了啥?”
  5. 如何给柱状图柱子添加阴影_【LeetCode日记】84. 柱状图中最大的矩形
  6. oracle 外部表 时间戳,Hive建立外部表与时间戳转换(含建dual表,修改列名,row_number() 函数等)...
  7. 让S3c2410里拥有HIVE注册表的 全部步骤
  8. Solaris 的防火墙ipfilter设置
  9. 英特尔Bridge技术加持下 Windows 11 PC也能运行手机应用
  10. 随想录(关于论文投稿)
  11. 站点简介(欢迎大家踊跃参与本站站点的建设,谢谢)
  12. delphi 如何判断 socket 连接成功_Linux下的C++ socket编程实例
  13. PID闭环控制系统的Simulink仿真
  14. Ubuntu 下查看图片
  15. Pollard’s rho 算法解大数质因子问题
  16. 中国移动开放平台(dev.cmccopen.cn)请求头Header:Authorization验证失败的原因(我遇到的)
  17. kafka connector使用(Docker一键启动版)
  18. 立方根c语言,在C ++中找到数字的立方根
  19. 再劫面包店(村上春树)
  20. C语言实现每次找出最大最小值排序

热门文章

  1. 2.[基础教程]数据结构 视频目录
  2. linux中chrony服务器的使用
  3. 实验吧ctf-web-让我进去(Hash长度扩展攻击)
  4. LWN:重构futex API!
  5. tomcat启动时报:IOException while loading persisted sessions: java.io.EOFException异常的解决办法
  6. Qt学习之定时器QTimer类编程
  7. OpenCV-Python实战(4)——OpenCV常见图像处理技术
  8. 【SQLyog错误号码2058解决办法】
  9. 关于Spark-Rapids的GPU化迁移至数据仓库的改造
  10. java8 matespace,聊聊jvm的PermGen与Metaspace