POI通过模板导出excel(包含表头合并处理)

news2024/11/19 1:29:27

最近接触到比较多poi相关的需求,总结一下通过模板导出以及表头合并的一些复杂情况处理。
简单使用的话可以参考下我之前写的 POI实现导入导出excel

目录:

  • 1、POI通过模板导出
  • 2、POI表头合并处理

1、POI通过模板导出

其实通过模板导出的原理,无非就是去获取到指定的模板文件,然后再去转换成文件流,最后填充自己的数据再转出excel文件。

先看下模板:(模板包含了一些样式,需要手动创建比较麻烦,所以采用模板转文件流的方式)
在这里插入图片描述

看下代码:

    public static void exportMain(String templatePath){

        //获取模板
        File file = new File(templatePath);

        InputStream is = null;

        XSSFWorkbook wb = null;

        XSSFSheet sheet = null;

        InputStream exportInput = null;

        try {

            is = new FileInputStream(file);// 将excel文件转为输入流

            wb = new XSSFWorkbook(is);// 创建个workbook,

            // 获取第一个sheet
            sheet = wb.getSheetAt(0);

            //例子是第四行开始表头
            XSSFRow row3 = sheet.getRow(3);

            //获取总列数
            int cellNum = row3.getLastCellNum();

            //获取第四行的列
            for (int i=0;i<cellNum;i++){
                XSSFCell cell = row3.getCell(i);
                System.out.println("第4行第"+(i+1)+"列:"+cell.toString());
            }

        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        exportMain("C:/Users/53065/Desktop/模板表.xlsx");
    }

打印结果:

第4行第1列:序号
第4行第2列:项目名称
第4行第3列:负责单位
第4行第4列:目前项目状态(完工、在建、前期)

第4行第5列:计划完工\开工时间
第4行第6列:建设内容
第4行第7列:进展情况
第4行第8列:2023年工作内容
第4行第9列:总投资
第4行第10列:截止2022年12月31日已完成投资
第4行第11列:
第4行第12列:
第4行第13列:
第4行第14列:2023年计划投资
第4行第15列:
第4行第16列:
第4行第17列:
第4行第18列:
第4行第19列:
第4行第20列:
第4行第21列:
第4行第22列:资金具体来源
第4行第23列:
第4行第24列:备注

从输出结果可以看出,模板的数据我们已经拿到,以上例子只是为了简单获取模板中的数据,通过模板再可以进行下一步的操作,这部分可以参考 POI实现导入导出excel

注意:XSSFSheet是获取xlsx文件格式,HSSFSheet是获取xls格式
请根据具体情况修改!这边只是比较简单的例子。

2、POI表头合并处理

在这里插入图片描述
我们从这部分表头可以发现,有合并多个单元格的表头。
在很多需求中,导出需要数据与表头对应,如果通过列的序列去指定这种方式,其实是很不稳妥的,在模板列顺序改变后就会出现问题,所以如果出现这种比较复杂的表头时,我们采用的做法是合并表头的拼接作为表头唯一标识,对应数据(例如 2023年计划投资-计划投资额-总额

那么需要怎么做呢?先来看个方法:(以上表头来看,我们需要处理的是第四行到第六行的表头

//获取单元格合并情况数
sheet.getNumMergedRegions();

//单元格合并情况
CellRangeAddress region = sheet.getMergedRegion(下标);
int firstRow = region.getFirstRow();//合并开始行
int firstColumn = region.getFirstColumn();//合并开始列
int lastColumn = region.getLastColumn();//合并结束列
int lastRow = region.getLastRow();//合并结束行

有了以上方法,我们的思路就是获取列合并的表头,只是行合并的表头过滤掉不处理,然后用map来存储,key为列的下标即可。

    public static void exportMain(String templatePath){

        //获取模板
        File file = new File(templatePath);

        InputStream is = null;

        XSSFWorkbook wb = null;

        XSSFSheet sheet = null;

        InputStream exportInput = null;

        try {

            is = new FileInputStream(file);// 将excel文件转为输入流

            wb = new XSSFWorkbook(is);// 创建个workbook,

            // 获取第一个sheet
            sheet = wb.getSheetAt(0);

            //存储表头合并部分
            Map<String,String> newBtMap = new HashMap<>();
            //获取合并部分
            for (int i=0;i<sheet.getNumMergedRegions();i++){
                CellRangeAddress region = sheet.getMergedRegion(i);
                int firstRow = region.getFirstRow();
                int firstColumn = region.getFirstColumn();
                int lastColumn = region.getLastColumn();
                int lastRow = region.getLastRow();
                //锁定表头且是列合并的,只是列合并的不管
                if (firstRow <3 || firstRow>5 || (lastColumn-firstColumn==0 && lastRow-firstRow>0)){
                    continue;
                }

                Row row = sheet.getRow(firstRow);
                Cell cell = row.getCell(firstColumn);
                String newBtName = cell.toString().trim();
                System.out.println("开始列:"+ firstColumn +",结束列:"+ lastColumn +",开始行:"+firstRow+",结束列:"+ lastRow+",值:"+newBtName);
                if (StringUtils.isEmpty(newBtName)){
                    continue;
                }
                for (int j=firstColumn;j<=lastColumn;j++){
                    String oldBtName = newBtMap.get(String.valueOf(j));
                    String newScBtName = "";
                    if (!StringUtils.isEmpty(oldBtName)){
                        newScBtName = oldBtName.trim() + "-";
                    }
                    newScBtName += newBtName ;
                    newBtMap.put(String.valueOf(j),newScBtName.replaceAll("\\s*|\r|\n|\t",""));
                }
            }

            System.out.println("合并表头:"+ JSONObject.toJSONString(newBtMap));

//            //例子是第四行开始表头
//            XSSFRow row3 = sheet.getRow(3);
//
//            //获取总列数
//            int cellNum = row3.getLastCellNum();
//
//            //获取第四行的列
//            for (int i=0;i<cellNum;i++){
//                XSSFCell cell = row3.getCell(i);
//                System.out.println("第4行第"+(i+1)+"列:"+cell.toString());
//            }

        }catch (Exception e){
            e.printStackTrace();
        }
    }

打印结果:

开始列:9,结束列:12,开始行:3,结束列:3,值:截止2022年12月31日已完成投资
开始列:13,结束列:20,开始行:3,结束列:3,值:2023年计划投资
开始列:21,结束列:22,开始行:3,结束列:3,值:资金具体来源
开始列:13,结束列:17,开始行:4,结束列:4,值:计划投资额
合并表头:{"11":"截止2022年12月31日已完成投资","22":"资金具体来源","12":"截止2022年12月31日已完成投资","13":"2023年计划投资-计划投资额","14":"2023年计划投资-计划投资额","15":"2023年计划投资-计划投资额","16":"2023年计划投资-计划投资额","17":"2023年计划投资-计划投资额","18":"2023年计划投资","19":"2023年计划投资","9":"截止2022年12月31日已完成投资","20":"2023年计划投资","10":"截止2022年12月31日已完成投资","21":"资金具体来源"}

这时会发现,这部分表头只包含了合并了列单元格的表头,那么还需要处理无单元格合并的情况。并将两种情况合并。

    public static void exportMain(String templatePath){

        //获取模板
        File file = new File(templatePath);

        InputStream is = null;

        XSSFWorkbook wb = null;

        XSSFSheet sheet = null;

        InputStream exportInput = null;

        try {

            is = new FileInputStream(file);// 将excel文件转为输入流

            wb = new XSSFWorkbook(is);// 创建个workbook,

            // 获取第一个sheet
            sheet = wb.getSheetAt(0);

            //获取表头
            Row btRow = sheet.getRow(3);
            //获取总列数
            int totalCellNum = btRow.getLastCellNum();

            //存储表头合并部分
            Map<String,String> newBtMap = new HashMap<>();
            //获取合并部分
            for (int i=0;i<sheet.getNumMergedRegions();i++){
                CellRangeAddress region = sheet.getMergedRegion(i);
                int firstRow = region.getFirstRow();
                int firstColumn = region.getFirstColumn();
                int lastColumn = region.getLastColumn();
                int lastRow = region.getLastRow();
                //锁定表头且是列合并的,只是列合并的不管
                if (firstRow <3 || firstRow>5 || (lastColumn-firstColumn==0 && lastRow-firstRow>0)){
                    continue;
                }

                Row row = sheet.getRow(firstRow);
                Cell cell = row.getCell(firstColumn);
                String newBtName = cell.toString().trim();
                System.out.println("开始列:"+ firstColumn +",结束列:"+ lastColumn +",开始行:"+firstRow+",结束列:"+ lastRow+",值:"+newBtName);
                if (StringUtils.isEmpty(newBtName)){
                    continue;
                }
                for (int j=firstColumn;j<=lastColumn;j++){
                    String oldBtName = newBtMap.get(String.valueOf(j));
                    String newScBtName = "";
                    if (!StringUtils.isEmpty(oldBtName)){
                        newScBtName = oldBtName.trim() + "-";
                    }
                    newScBtName += newBtName ;
                    newBtMap.put(String.valueOf(j),newScBtName.replaceAll("\\s*|\r|\n|\t",""));
                }
            }

            System.out.println("合并表头:"+ JSONObject.toJSONString(newBtMap));


            //取出无合并情况的表头(4-6行)
            Map<String,String> btMap = new HashMap<>();
            for (int j=3;j<6;j++){
                Row row = sheet.getRow(j);
                for (int i=0;i<totalCellNum;i++){
                    Cell cell = row.getCell(i);
                    if (StringUtils.isEmpty(cell.toString())){
                        continue;
                    }
                    String cellValue = cell.toString().trim().replaceAll("\\s*|\r|\n|\t","");
                    System.out.println("第"+(j+1)+"行第"+(i+1)+"列数据:"+ cellValue);
                    btMap.put(String.valueOf(i),cellValue);
                }
            }

            System.out.println("无合并情况表头:"+JSONObject.toJSONString(btMap));

            //合并所有表头
            //遍历拼接表头去拼接全部数据
            Set<String> newBtKeys = newBtMap.keySet();
            for (String key : newBtKeys){
                //最后一行表头
                String lastBtName = btMap.get(key);
                //除最后一行外的表头
                String cLastBtName = newBtMap.get(key);
                //最全的表头
                String allBtName = cLastBtName;
                if (!StringUtils.isEmpty(lastBtName) && !cLastBtName.equals(lastBtName)){
                    allBtName += "-"+lastBtName;
                }
                btMap.put(key,allBtName);
            }

            System.out.println("导出模板拼接表头:"+JSONObject.toJSONString(btMap));

        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        exportMain("C:/Users/53065/Desktop/模板表.xlsx");
    }

打印结果:

开始列:9,结束列:12,开始行:3,结束列:3,值:截止2022年12月31日已完成投资
开始列:13,结束列:20,开始行:3,结束列:3,值:2023年计划投资
开始列:21,结束列:22,开始行:3,结束列:3,值:资金具体来源
开始列:13,结束列:17,开始行:4,结束列:4,值:计划投资额
合并表头:{"11":"截止2022年12月31日已完成投资","22":"资金具体来源","12":"截止2022年12月31日已完成投资","13":"2023年计划投资-计划投资额","14":"2023年计划投资-计划投资额","15":"2023年计划投资-计划投资额","16":"2023年计划投资-计划投资额","17":"2023年计划投资-计划投资额","18":"2023年计划投资","19":"2023年计划投资","9":"截止2022年12月31日已完成投资","20":"2023年计划投资","10":"截止2022年12月31日已完成投资","21":"资金具体来源"}
第4行第1列数据:序号
第4行第2列数据:项目名称
第4行第3列数据:负责单位
第4行第4列数据:目前项目状态(完工、在建、前期)
第4行第5列数据:计划完工\开工时间
第4行第6列数据:建设内容
第4行第7列数据:进展情况
第4行第8列数据:2023年工作内容
第4行第9列数据:总投资
第4行第10列数据:截止2022年12月31日已完成投资
第4行第14列数据:2023年计划投资
第4行第22列数据:资金具体来源
第4行第24列数据:备注
第5行第10列数据:已投资总额
第5行第11列数据:1.财政安排
第5行第12列数据:2.融资
第5行第13列数据:3.自筹
第5行第14列数据:计划投资额
第5行第19列数据:1.财政安排
第5行第20列数据:2.自筹
第5行第21列数据:3.融资
第5行第22列数据:1.自筹
第5行第23列数据:2.融资
第6行第14列数据:总额
第6行第15列数据:一季度
第6行第16列数据:二季度
第6行第17列数据:三季度
第6行第18列数据:四季度
无合并情况表头:{"11":"2.融资","22":"2.融资","23":"备注","12":"3.自筹","13":"总额","14":"一季度","15":"二季度","16":"三季度","17":"四季度","18":"1.财政安排","19":"2.自筹","0":"序号","1":"项目名称","2":"负责单位","3":"目前项目状态(完工、在建、前期)","4":"计划完工\\开工时间","5":"建设内容","6":"进展情况","7":"2023年工作内容","8":"总投资","9":"已投资总额","20":"3.融资","21":"1.自筹","10":"1.财政安排"}
导出模板拼接表头:{"11":"截止2022年12月31日已完成投资-2.融资","22":"资金具体来源-2.融资","23":"备注","12":"截止2022年12月31日已完成投资-3.自筹","13":"2023年计划投资-计划投资额-总额","14":"2023年计划投资-计划投资额-一季度","15":"2023年计划投资-计划投资额-二季度","16":"2023年计划投资-计划投资额-三季度","17":"2023年计划投资-计划投资额-四季度","18":"2023年计划投资-1.财政安排","19":"2023年计划投资-2.自筹","0":"序号","1":"项目名称","2":"负责单位","3":"目前项目状态(完工、在建、前期)","4":"计划完工\\开工时间","5":"建设内容","6":"进展情况","7":"2023年工作内容","8":"总投资","9":"截止2022年12月31日已完成投资-已投资总额","20":"2023年计划投资-3.融资","21":"资金具体来源-1.自筹","10":"截止2022年12月31日已完成投资-1.财政安排"}

从最终结果可以看到处理成功啦

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/82325.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2021 年年度最佳开源软件

Svelte https://svelte.dev/ Svelte 是一种全新的构建用户界面的方法。传统框架如 React 和 Vue 在浏览器中需要做大量的工作&#xff0c;而 Svelte 将这些工作放到构建应用程序的编译阶段来处理。 与使用虚拟&#xff08;virtual&#xff09;DOM 差异对比不同。Svelte 编写的…

DAla-Gly-Gly,77286-90-9

Substrate for bacterial D-aminopeptidases.细菌d -氨基肽酶的底物。 编号: 191616中文名称: 三肽DAla-Gly-Gly英文名: DAla-Gly-GlyCAS号: 77286-90-9单字母: H2N-DAla-GG-OH三字母: H2N-DAla-Gly-Gly-COOH氨基酸个数: 3分子式: C7H13N3O4平均分子量: 203.2精确分子量: 203.0…

虚拟现实 VR 碰撞 3D 可视化,图扑打造一体化管控平台

工信部《虚拟现实产业发展白皮书 5.0 》中明确提出&#xff1a;“通过财政资金促进虚拟现实技术产业化&#xff0c;支持面向工业、文化、教育等重点行业的虚拟现实技术应用”。 虚拟现实 VR 技术以用户体验视角为中心&#xff0c;跟踪反馈在 3D 场景中的动作&#xff0c;借助软…

kali工具学习

目录 1.waybackurls 网络爬虫&#xff0c;url搜索 2.wafw00f 检测网站用了什么waf 3.theHarvester ip、邮箱、host搜集 4. oneforall 子域名搜集 5. nmap kali自带的扫描工具 6. whatweb 指纹查询 1.waybackurls 网络爬虫&#xff0c;url搜索 2.wafw00f 检测网…

阿里巴巴注册中心nacos

目录 一、配置中心介绍 1、Spring Cloud Config 2、Nacos替换Config &#xff08;1&#xff09;应用场景 二、读取Nacos配置中心的配置文件 1、在Nacos创建统一配置文件 2、以service-statistics模块为例 3、补充&#xff1a;springboot配置文件加载顺序 三、名称空间…

网络实验之VRRP协议

一、VRRP协议简介 虚拟路由冗余协议(Virtual Router Redundancy Protocol&#xff0c;简称VRRP)是由IETF提出的解决局域网中配置静态网关出现单点失效现象的路由协议。VRRP是一种路由容错协议&#xff0c;也可以叫做备份路由协议。一个局域网络内的所有主机都设置缺省路由&…

MySQL入门阶段这一篇就够了-学习笔记(手敲1.5万字)

前言 虽然在大一下学期&#xff0c;就已经接触到了MySQL&#xff0c;但是那个时候只是会用MySQL进行增删改查&#xff0c;在大三上学期&#xff0c;尝试投简历寻找实习时&#xff0c;对方公司对于程序员的MySQL水平有很高的要求&#xff0c;所以我开始系统化的学习MySQL。顺便…

想写出复用性强的组件?快来试试 Storybook 吧

简介 Storybook 是 UI 组件的开发环境&#xff0c;它允许开发者浏览组件库&#xff0c;查看每个组件的不同状态&#xff0c;以及交互地开发和测试组件。 Storybook 可帮助你记录组件以供重用&#xff0c;并自动对组件进行可视化测试以防止出现错误。 开始 在对 Storybook 有…

halcon measure_pos和measure_pairs详解

这两个算子作为halcon一维测量的关键&#xff0c;看了这两个算子的多个博客&#xff0c;也没搞清楚里面几个参数的意义和测量的原理&#xff0c;决定自己详细试一下&#xff0c;将测试的过程详细记录下来&#xff0c;供以后需要参考的朋友使用&#xff0c;本文将从以下几个点说…

使用bat脚本运行python脚本,bat出现闪退,怎么解决?

前言 本文是该专栏的第4篇,后面会持续分享python的各种干货知识,值得关注。 工作上为了方便,很多时候我们会设置一个bat脚本来双击运行python脚本,这样既方便又能在工作上节约大量时间。 但是,你或许会碰到这样的情况,当双击bat脚本的时候,bat窗口界面会直接出现闪退关…

DM8设置自动备份,达梦数据库,自动备份。详细步骤。常用命令,启动关闭数据库,查看DMAP状态

常用命令 DmServiceDAMENG.service改成自己bin目录下文件名 运维常见问题&#xff0c;官方文档 注意事项 登录客户端工具&#xff0c;一定要使用SYSDBA用户进行下面的客户端操作 登录数据库主机&#xff0c;一定要使用安装数据库时创建的dmdba用户 定时备份前请先配置本地…

引入新模块都在用这个注解,它是如何生效的?|原创

本文讲解了Enable 类注解是如何生效的以及其核心注解 Import 的原理&#xff0c;并且用 EnableAsync 注解来举例。点击上方“后端开发技术”&#xff0c;选择“设为星标” &#xff0c;优质资源及时送达在项目开发的过程中&#xff0c;我们会遇到很多名字为 Enablexxx 的注解&a…

OA系统解决方案

一、OA系统解决方案介绍 OA系统&#xff0c;即办公自动化系统&#xff08;Office Assistant简称OA&#xff09;&#xff0c;它是一个集成了企业信息发布、公文与信息管理、公文处理、知识管理、内部通讯、协同办公等办公与管理应用功能一体的协同 办公系统。OA系统解决方案则是…

Linux——详解共享内存shared memory

目录 一.共享内存介绍 &#xff08;一&#xff09;.什么是共享内存 &#xff08;二&#xff09;.共享内存优点 &#xff08;三&#xff09;.共享内存缺点 二.共享内存使用 &#xff08;一&#xff09;.创建—shmget ①key ②size ③shmflg ④返回值 &#xff08;二&…

CRMEB电商商城系统腾讯云ECS服务器安装配置搭建教程文档

一、推荐使用宝塔Linux面板&#xff0c;简单好用。二、放行服务器端口。详细步骤&#xff1a; 1.登录腾讯云服务器&#xff0c;点击右上角“控制台” 2.我的资源&#xff0c;点击进入云服务器 3.进入实例列表&#xff0c;选择您要安装的服务器&#xff0c;点击更多 4.选择重装…

Hadoop 如何保证自己的江湖地位?Yarn 功不可没

前言 任何计算任务的运行都离不开计算资源&#xff0c;比如 CPU、内存等&#xff0c;那么如何对于计算资源的管理调度就成为了一个重点。大数据领域中的 Hadoop 之所以一家独大&#xff0c;深受市场的欢迎&#xff0c;和他们设计了一个通用的资源管理调度平台 Yarn 密不可分&a…

Metal每日分享,四维向量偏移滤镜效果

本案例的目的是理解如何用Metal实现图像4维向量颜色效果滤镜,通过对像素点颜色进行4维向量叠加运算得到新的像素点; Demo HarbethDemo地址实操代码 // 暖色系 let filter = C7ColorVector4(vector: Vector4.Color.warm)// 方案1: ImageView.image = try? BoxxIO(element: o…

浅谈字节码增强技术系列1-字节码增强概览

作者&#xff1a;董子龙 前言 前段时间一直想参照lombok的实现原理写一篇可以生成业务单据修改记录插件的专利&#xff0c;再查阅资料的过程中&#xff0c;偶然了解到了字节码增强工具-byteBuddy。但是由于当时时间紧促&#xff0c;所以没有深入的对该组件进行了解。其实再我…

一文搞定Pandas核心概念之DataFrame

DataFrame概述 DataFrame 是一个表格型的数据结构&#xff0c;它含有一组有序的列&#xff0c;每列可以是不同的值类型&#xff08;数值、字符串、布尔型值&#xff09;。DataFrame 既有行索引也有列索引&#xff0c;它可以被看做由 Series 组成的字典&#xff08;共同用一个索…

麒麟系统下基于卫星的NTP网络授时服务器方案

麒麟系统下基于卫星的NTP网络授时服务器方案 1、 麒麟系统NTP授时方案 设计思路&#xff1a; 在通用的麒麟服务器内部固定一块北斗卫星接收模块并引出卫星天线接口&#xff0c;卫星模块接收北斗卫星数据并解码输出时间数据&#xff08;NMEA0183串口数据&#xff09;&#xff…