通过jxls + poi 解决导出excel表格和动态合并单元格

2023-11-06

技术:jxls:通过模板导出数据;

           poi:解决第一列中相同值的单元格合并;

第一步:依赖导入

<dependency>
    <groupId>net.sf.jxls</groupId>
    <artifactId>jxls-core</artifactId>
    <version>1.0.6</version>
</dependency>

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>3.17</version>
</dependency>

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>3.17</version>
</dependency>

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml-schemas</artifactId>
    <version>3.17</version>
</dependency>

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-scratchpad</artifactId>
    <version>3.15</version>
</dependency>

第二部:上传需求规定的excel模板

模板编辑:

源码:

 将模板存到数据库:

//Controller 层

@RequestMapping(value = "/saveMixedShipExTemplate", method = RequestMethod.POST)
	public JsonResult saveMixedShipExTemplate (@RequestParam("file") MultipartFile file) throws IOException{
		InputStream is = file.getInputStream();
		boolean rs = templateService.saveMixedShipExTemplate(is);
		try {
			is.close();
		} catch (IOException e) {
			log.debug("InputStream关闭失败!");
		}
		if (rs) {
			return JsonResult.success();
		}else {
			return JsonResult.error(ReturnEnum.FAILURE);
		}
	}
//返回值类型
public class JsonResult<T> implements Serializable {
    private static final long serialVersionUID = 2652608027682835212L;
    private boolean success;
    private String errorCode = "";
    private String message = "";
    private T data;

//Service 层

public boolean saveMixedShipExTemplate(InputStream inputStream) throws IOException {
        if (inputStream == null) {
            return false;
        }
        byte[] data = inputStreamToByte(inputStream);//将文件保存到字节数组中
        Template template = new Template();
        template.setFileName("exportMixedShipAndInfo");
        template.setFileContent(data);
        TemplateDomain templateDomain = new TemplateDomain(template);
        // 校验是否已存在模板,若存在则更新,否则插入新模板
        BigDecimal id = templateMapper.countTemplate("exportMixedShipAndInfo");
        if (id == null) {// 新增
            templateDomain.create();
            templateMapper.saveTemplate(template);
        }else {// 更新
            template.setId(id);
            templateDomain.update();
            templateMapper.updateTemplateById(template);
        }
        return true;
    }

//定义Domian设置创建人、创建时间,更新时间...
public class TemplateDomain extends BaseDomain<Template>{

	public TemplateDomain(Template e) {
		super(e);
	}

//读取InputStream的内容并保存到byte数组
    private byte [] inputStreamToByte(InputStream is) throws IOException {
        ByteArrayOutputStream bAOutputStream = new ByteArrayOutputStream();
        int ch;
        while((ch = is.read() ) != -1){
            bAOutputStream.write(ch);
        }
        byte data [] =bAOutputStream.toByteArray();
        bAOutputStream.close();
        return data;
    }

	public void create() {
		BigDecimal userId = getUserId();
		po.setId(generateNewId());
		po.setCreateTime(now());
		po.setCreateUserid(userId);
		po.setUpdateTime(po.getCreateTime());
		po.setUpdateUserid(userId);
		po.setIsDelete(Constant.DEFAULT_NUM_0);
	}
	
	public void update() {
		BigDecimal userId = getUserId();
		po.setUpdateTime(now());
		po.setUpdateUserid(userId);
		po.setIsDelete(Constant.DEFAULT_NUM_0);
	}
}
public abstract class BaseDomain<E> {
    protected E po;
    private static final IdWorker idWorker = IdWorker.getInstance();

    protected BaseDomain(E e) {
        this.po = e;
    }

    public E getPO() {
        return po;
    }

    protected BigDecimal generateNewId() {
        return idWorker.nextId();
    }

    protected Date now() {
        return new Date();
    }

    protected BigDecimal getUserId() {
        String userId = BaseContextHandler.getUserID();
        if (CommonValidator.checkStringEmpty(userId))
            return null;

        return new BigDecimal(userId);
    }

    protected String getTenantId() {
        String tenantId = BaseContextHandler.getTenantId();
        if (CommonValidator.checkStringEmpty(tenantId) || tenantId==null) {
            return "DeFault";
        }
        return tenantId;
    }
}

第三部:编写导出程序

//Service 层   
 public void exportMixedShipAndInfo(BigDecimal id) throws Exception {
        //获取列表数据
        MixedShipResult mixedShipAndInfo = this.getMixedShipAndInfo(id);
        Map<String, Object> exeMap = new HashMap<String, Object>();
        exeMap.put("entity", mixedShipAndInfo);
        exeMap.put("list", mixedShipAndInfo.getInfoList());
        //获取导出模板
            Map<String, Blob> map = templateMapper.getTemplate("exportMixedShipAndInfo");
        byte[] byt = blobToBytes(map.get(Constant.TEMPLATE_FIELD_FILE_CONTENT));
        InputStream is = new ByteArrayInputStream(byt);
        //将数据传入模板中
        XLSTransformer transformer = new XLSTransformer();
        Workbook workbook = transformer.transformXLS(is, exeMap);

        //动态合并单元格需要的逻辑
        String[] names = {"单位名称"};
        List<String> needColNames = Arrays.asList(names);
        ExcelUtils.mergeSameCellByMainCol(workbook, needColNames, 4);

        //导出excel
        ExportUtil exportUtil=new ExportUtil();
        exportUtil.export(workbook);
    }

通过模板名称获取模板

  <select id="getTemplate" resultType="java.util.Map" >
    select FILE_CONTENT
    from CLOUD_UPLOAD_TEMPLATE
    where FILE_NAME = #{name,jdbcType=VARCHAR} and is_delete=0 and rownum=1
    order by update_time desc
  </select>

其中公共类:ExcelUtils 和  ExportUtils

@Slf4j
public class ExportUtil {

    /**
     * @param workbook,fileName
     * @author 杨光
     * @description 导出公用方法
     * @date 创建时间:2020年4月7日
     */
    public void export(Workbook workbook) throws Exception {
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        response.setContentType("application/vnd.ms-excel;charset=utf-8");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-disposition", "attachment; filename=export.xls" );
        response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
        response.setHeader("Pragma", "public");
        response.setDateHeader("Expires", System.currentTimeMillis() + 1000L);
        response.setContentType("application/vnd.ms-excel;charset=utf-8");
        OutputStream outputStream = response.getOutputStream();
        workbook.write(outputStream);
    }

}
//合并单元格公共类
public class ExcelUtils {
// list 的第一个为需要做合并判断逻辑的 , list中其他的合并逻辑跟第一个相同
    public static Workbook mergeSameCellByMainCol(Workbook workbook, List<String> needMergeColName, Integer startMergeRowNum) {

        List<MergeRecord> mergeRecords = new ArrayList<>();
        List<Integer> needMergeColNum = new ArrayList<>();
        Sheet sheet = workbook.getSheetAt(0);
        if (workbook != null && needMergeColName != null) {

            // 遍历获取所有相同单元格,并记录
            // 遍历表头,获取需要合并的 colnum ,
            Row firstRow = sheet.getRow(2);
            if (firstRow != null) {
                // 找出所有需要合并单元格的列号
                for (String needName : needMergeColName) {
                    for (int i = 0; i < firstRow.getLastCellNum(); i++) {
                        String cellContent = firstRow.getCell(i).getStringCellValue();
                        if (StringUtils.isNotEmpty(cellContent)) {
                            if (cellContent.equals(needName)) {
                                needMergeColNum.add(i);
                                break;
                            }
                        }
                    }
                }
            }
            // 找出所有需要合并的单元格
            Integer needCol = needMergeColNum.get(0);
            Integer allRows = sheet.getLastRowNum();
            String sameContent = null;
            Integer firstRowNum = null;
            Integer lastRowNum = null;
            Integer firstColNum = null;
            Integer lastColNum = null;
            MergeRecord mergeRecord = null;
            for (int rowNum = startMergeRowNum; rowNum < allRows + 1; rowNum++) {
                // 如果本行与下一行的值一样 ,则需要新建一个合并区域
                Row row = sheet.getRow(rowNum);
                Row nextRow = sheet.getRow(rowNum + 1);
                String cellVal = row.getCell(needCol).getStringCellValue();
                String nextCellVal = null;
                if (nextRow != null) {
                    nextCellVal = nextRow.getCell(needCol).getStringCellValue();
                }
                // 如果当行值等于相同值的话 ,合并行加1
                if (sameContent != null && sameContent.equals(nextCellVal)) {
                    lastRowNum = rowNum + 1;
                    continue;
                }
                // 如果下一行的值与当前值不相同 或者已经到最后一行,那么结束本次的合并单元格任务
                if (((sameContent != null && !sameContent.equals(nextCellVal))) || (nextRow == null && sameContent != null)) {
                    mergeRecord = new MergeRecord();
                    mergeRecord.setMergeContent(sameContent);
                    mergeRecord.setCulomnNum(needCol);
                    mergeRecord.setFirstRow(firstRowNum);
                    mergeRecord.setLastRow(lastRowNum);
                    mergeRecord.setFirstCol(firstColNum);
                    mergeRecord.setLastCol(lastColNum);
                    mergeRecords.add(mergeRecord);

                    // 将相同内容置空 ,并寻找下一个需要合并的单元格
                    sameContent = null;
                }
                // 如果第一次两个值一样的话
                if (cellVal.equals(nextCellVal)) {
                    sameContent = cellVal;
                    firstRowNum = rowNum;
                    lastRowNum = rowNum + 1;
                    firstColNum = needCol;
                    lastColNum = needCol;
                }
            }
            // 其他列的合并情况跟第一列都一样
            List<MergeRecord> lastAll = new ArrayList<>();
            for (int i = 1; i < needMergeColNum.size(); i++) {
                List<MergeRecord> tempList = new ArrayList<>();
                for (MergeRecord record : mergeRecords) {
                    MergeRecord mergeRecord1 = new MergeRecord();
                    mergeRecord1.setFirstRow(record.getFirstRow());
                    mergeRecord1.setLastRow(record.getLastRow());
                    mergeRecord1.setFirstCol(needMergeColNum.get(i));
                    mergeRecord1.setLastCol(needMergeColNum.get(i));

                    tempList.add(mergeRecord1);
                }
                lastAll.addAll(tempList);
            }
            mergeRecords.addAll(lastAll);
        }
        // 找完所有的单元格后 ,开始合并
        for (MergeRecord mergeRecord : mergeRecords) {
            sheet.addMergedRegion(new CellRangeAddress(mergeRecord.getFirstRow(), mergeRecord.getLastRow(), mergeRecord.getFirstCol(), mergeRecord.getLastCol()));
        }
        return workbook;
    }

/**
     *
     * @Title: blobToBytes
     * @Description: 将Blob转换为byte数组
     * @param: @param blob
     * @param: @return
     * @return: byte[]
     * @throws
     */

public static byte[] blobToBytes(Blob blob) {
        BufferedInputStream is = null;
        try {
            is = new BufferedInputStream(blob.getBinaryStream());
            byte[] bytes = new byte[(int) blob.length()];
            int len = bytes.length;
            int offset = 0;
            int read = 0;
            while (offset < len
                    && (read = is.read(bytes, offset, len - offset)) >= 0) {
                offset += read;
            }
            return bytes;
        } catch (Exception e) {
            return null;
        } finally {
            try {
                is.close();
                is = null;
            } catch (IOException e) {
                return null;
            }
        }
    }
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

通过jxls + poi 解决导出excel表格和动态合并单元格 的相关文章

  • Excel VBA 更改命令按钮的颜色

    我在更改颜色时遇到问题CommandButton 在电子表格中 我添加设计按钮作为表单或 ActiveX 然后在 VBA 中我尝试 Activesheet shapes CommandButton1 visible false 这个效果很好
  • 过滤所有独特的项目,例如 Google 文档

    是否有一种快速 简单的方法来过滤 Excel 2013 列中的所有唯一项目 类似于 Google Docs 唯一 功能 这不是一个漂亮的答案 但它有效 将其作为数组公式粘贴到单元格中B2 LOOKUP 2 1 COUNTIF B 1 B1
  • 如何下载到 Excel?

    我想为我的 Coldfusion 网站不同部分上的几组不同数据提供 下载到 Excel 功能 我正在使用 Coldfusion 并且希望使用免费的自定义标签 库来帮助我完成此任务 而不是自己从头开始编码 我被指出cflib org http
  • =MATCH() 等价于多维范围

    我有一个 Excel 工作表 其中单元格 A1 C20 INT RAND 10 这是我的数据范围 单元格 E1 1 E2 2 E3 3 等 这些是我试图找到的值 我设置单元格 F1 MATCH E1 A C 0 F2 MATCH E1 A
  • Interop.Excel 和 Tools.Excel 之间的区别?

    我目前正在开发 Microsoft Excel 的插件 但我对某些事情有点困惑 两者有什么区别Interop Excel and Tools Excel 例如 之间Interop Excel Workbook and Tools Excel
  • 将图表导出为图像有时会生成空文件

    I m doing a macro that exports all the charts in the sheet and then opens Outlook and attaches them However I ve noticed
  • 使用VBA复制垂直列并沿对角线粘贴

    我有一列数据 我们称之为 A 列 其中有 35 行数据 如何在此列上循环 然后将每个数据点粘贴到另一张工作表中 同时为每个循环循环增加列和行 换句话说 我寻求对角粘贴在第二张纸中 有没有一种简单的方法可以在 VBA 中执行此类操作 不要循环
  • phpexcel xlsx 的千位分隔符

    我在用着 PHPExcel Shared String setThousandsSeparator 为 Excel 文件定义千位分隔符 多亏了它 单元格显示为55 452代替55452 不过 单元格值是整数 55452 因此可以对其进行计算
  • 文本到行 VBA Excel

    我有一个电子表格 其中包含大约 4000 行数据 其中一列数据具有唯一的订单号 我希望使用 作为分隔符将其分隔 所以本质上我想要 Name Order Date Jane 123 001 111 08 15 2013 Gary 333 12
  • excel 2010刷新BackgroundQuery中运行时错误1004

    我正在尝试用 vba 编写一个脚本 用于将多个文本文件导入 Excel 一张纸 然后将它们绘制在一张图表上 我面临一个问题刷新后台查询命令并出现 1004 运行时错误 我怎样才能解决它 谢谢 埃亚勒 这是我的代码 Sub fring1 Di
  • Excel 工作簿 - 从 C# 读取速度非常慢?

    正在尝试读取 Excel 工作簿 发现读取 3560 行 7 列的工作表需要很长时间 大约需要 1 分 17 秒 我所做的就是循环遍历整个工作表并将值存储在列表中 这是正常现象 还是我做错了什么 static void Main strin
  • VBA 中的多线程

    这里有人知道如何让VBA运行多线程吗 我正在使用 Excel 无法用 VBA 本地完成 VBA 构建在单线程单元中 获得多个线程的唯一方法是使用 VBA 之外的其他具有 COM 接口的东西构建 DLL 并从 VBA 调用它 信息 OLE 线
  • 导出到excel时如何显示前导零?

    我正在通过更改内容类型来创建 Excel 报告 Response ContentType application vnd ms excel 我有包含前导零的值 问题是导出到 Excel 时缺少前导零 e g 000123 gt 123 我知
  • 使用 ObjPtr(Me) 返回自定义类实例的名称?

    我明白那个ObjPtr http support microsoft com kb 199824将返回内存中对象的地址 并且它指向一个名为 IUNKNOWN 的结构 并且其中编码了某种接口定义以公开对象结构 但我不知道如何确定一个对象的接口
  • 我可以获取VBA代码中的注释文本吗

    可以说我有以下内容 Public Sub Information TEST End Sub 有没有办法得到 TEST 结果 不知何故通过VBA 例如 在 PHP 中 有一个获取注释的好方法 这里有什么想法吗 编辑 应该有办法 因为像 MZ
  • Excel 数字缩写格式

    这是我想要完成的任务 Value Display 1 1 11 11 111 111 1111 1 11k 11111 11 11k 111111 111 11k 1111111 1 11M 11111111 11 11M 11111111
  • 使用 FindElementbyXpath() 获取 Selenium Basic 中可填充框的行和列名称

    我正在使用 Selenium Basic 将电子表格中的文本填充到网站中 网站的html代码是这样的 div table cellspacing 0 border 1 style width 99 tr th style font weig
  • 根据列值突出显示数据框中的行?

    假设我有这样的数据框 col1 col2 col3 col4 0 A A 1 pass 2 1 A A 2 pass 4 2 A A 1 fail 4 3 A A 1 fail 5 4 A A 1 pass 3 5 A A 2 fail 2
  • 使用PHP从doc、xls文件中读取数据

    我想知道是否可以从 doc 和 xls 文件中读取数据并将 将内容读取到图像文件中 创建文档的页面样本 例如 我有一些文件希望我的客户购买 所以我需要自动创建小图像 例如我的文档样本 我们将不胜感激您的帮助 对于读取 xls 文件 我真的推
  • 如何将 .xlsx 文件上传到 jenkins 作业

    如何将 xlsx 文件作为构建参数上传到 jenkins 作业 我尝试使用文件参数 但我发现该文件正在丢失其扩展名或原始格式 有什么方法可以从 jenkins UI 将 excel 文件上传到 jenkins 作业吗 In the file

随机推荐

  • 读取excel文件转为HTML

    最近写了一个公式较复杂的报表 因梳理公式与后期的数据核对会比较耗费时间 因此采用了读取EXCEL模板 然后填写数据 最后读取最终文件生成页面的方法 现整理了将EXCEL转为HTML的方法 本人在用的xlsx文件 颜色不支持灰色 建议使用其他
  • el-date-picker日期限定范围

    el date picker日期限定范围 页面
  • 一文看懂RPA的技术架构及原理

    一文看懂RPA的技术架构及原理 众所周知 作为一款软件或平台 RPA 机器人流程自动化 是用来替代人类员工实施基于规则的高度重复性工作的程序 而非实体存在的流程处理机器 但由于它的新颖性 许多人可能会对RPA及其构成感到困惑 典型的RPA平
  • VS 2015 新建QT项目

    打开VS 2015 文件 新建 新建项目 选择QT5 Projects QT Application 没有出现上述菜单的 请重新安装Visual Studio Add in 1 2 4 for Qt5 并重启VS QT创建向导 选择必要组件
  • 分布式服务高可用实现:复制

    1 为什么需要复制 我们可以考虑如下问题 当数据量 读取或写入负载已经超过了当前服务器的处理能力 如何实现负载均衡 希望在单台服务器出现故障时仍能继续工作 这该如何实现 当服务的用户遍布全球 并希望他们访问服务时不会有较大的延迟 怎么才能统
  • Java异常学习总结

    1 什么是异常 异常本质上是程序上的错误 包括程序逻辑错误和系统错误 在Java中异常被当做对象来处理 根类是java lang Throwable类 在Java中定义了很多异常类 如OutOfMemoryError NullPointer
  • 性能测试的目的

    目的是验证软件系统是否能够达到用户提出的性能指标 同时发现软件系统中存在的性能瓶颈 优化软件 最后起到优化系统的目的 包括以下几个方面 1 评估系统的能力 测试中得到的负荷和响应时间数据可以被用于验证所计划的模型的能力 并帮助作出决策 2
  • Django 在Django项目里单独运行某个py文件

    Python文件开头写以下代码 import os import django 在environ字典里设置默认Django环境 xxxx settings 指Django项目的配置文件 os environ setdefault DJANG
  • echarts渐变色实现方法

    我使用的是echarts 4 2版本 在管网的文档中可查看其配置项 以柱状图为例 首先在series中找type line 然后找到areaStyle 在color中有方法能生成渐变色 线性渐变 前四个参数分别是 x0 y0 x2 y2 范
  • IT项目管理——项目范围(附带案例,案例为本人项目,并非传统案例)

    IT项目管理 项目范围 项目范围理论部分 前言 项目范围阐述 项目产品范围与项目工作范围的关系 项目范围控制 项目范围控制能带来什么 项目范围控制步骤 6个阶段 案例实战 须知 项目范围阐述 项目范围控制 项目范围管理的困难 工作分解结构
  • IOl流写一段文本存到本地文件中

    一 FileOutputStream中的小细节 1 创建对象 细节1 参数是字符串表示的路径或者File对象都是可以哦 细节2 如果文件不存在 会创建一个新的文件 但是要保证父级路径是正确的 细节3 如果文件已经存在 则会清空文件 2 写出
  • java设计模式——策略模式(Strategy Pattern)

    概述 在策略模式中 我们可以定义一些独立的类来封装不同的算法 每一个类封装一种具体的算法 在这里 每一个封装算法的类我们都可以称之为一种策略 Strategy 为了保证这些策略在使用时具有一致性 一般会提供一个抽象的策略类来做规则的定义 而
  • openssl命令基础用法:生成密码

    生成密码需要使用的标准命令为 passwd 用法如下 openssl passwd crypt 1 apr1 salt string in file stdin noverify quiet table password 常用选项有 1 使
  • Matlab实现回归分析的案例

    下面是一个简单的线性回归分析的Matlab实现案例 假定我们有一组数据 其中一个变量是自变量 另一个变量是因变量 我们想要使用线性回归来建立两个变量之间的关系模型 假设我们有以下数据 x 1 2 3 4 5 y 1 5 3 5 4 5 7
  • 查看url里面是否存在某个参数js

    场景 最近在做一个H5微信公众号的开发 微信分享出去的东西 用户点击进入 使用微信code授权之后 还是总提示code无效 code已使用的问题 原因 用户点击进入之后 会先进行登录 会获取到一次code 但是 在进行其他操作时再次用到这个
  • kafka学习(五):消费者分区策略(再平衡机制)

    kafka再平衡机制 指的是kafka consumer锁订阅的topic发生变化时 发生的一种分区重分配机制 一般有三种情况会出发consumer的分区分配策略 再平衡机制 1 consumer group 中新增或删除某个consume
  • java 将word转为pdf文件的两种方式【spire.doc.free】【documents4j】

    场景 如资产证明等场景下 一般要求同时生成word与pdf两种格式的证明文件 且两者格式需保持一致 可以各自单独生成 但那样可能需要维护两个模板文件 所以也可以仅定义一份word的模板文件 使用模板生成word文件 再将word转换为pdf
  • cmake中文手册pdf_【电脑技巧】第50期:来了!Python3.8.3官方中文手册

    介绍 Python 作为一门相当简洁的计算机语言 目前已经拥有诸多用户 而英文的帮助文件或多或少给英语水平不太好的用户带来了一定的不便 事实上 官网就有中文手册 为了大家的方便 这里下载下来中英两种格式的PDF文件 并合并为一个带书签的总文
  • mysql关系运算选择投影连接,数据库关系代数操作 并 差 积 选择 投影 连接等操作...

    首先我们需要明白 关系代数操作分为下面两种操作 并操作 首先需要满足并相容性 并相容性指的是 关系R与关系S存在相容性 当且仅当 1 关系R和关系S的属性数目必须相同 2 对于任意i 关系R的第i个属性的域必须和关系S的第i个属性的域相同
  • 通过jxls + poi 解决导出excel表格和动态合并单元格

    技术 jxls 通过模板导出数据 poi 解决第一列中相同值的单元格合并 第一步 依赖导入