基于EasyExcel锁定指定列导出数据到excel

news2024/10/7 10:24:13

基于EasyExcel锁定指定列导出数据到excel

大家好,我是llp。最近在做系统报表时中有一个需求时这样的,需要查询系统数据导出excel,并要求导出的excel列中有一些时锁定的有一些时不锁定的,即使实现动态列锁定的效果。看上去应该是一个比较简单的需求,但就是这样一个需求我停滞了许久,特此做个记录顺带做个分享。

1.需求描述

要求导出的excel列中有一些时锁定的有一些时不锁定的,即使实现动态列锁定的效果。

  • 需求图示

image-20221129185658805

  • easyexcel基本使用: https://blog.csdn.net/qq_44981526/article/details/122052455

2.实现步骤

1.获取要导出的数据

示例代码

@GetMapping("/exportRiskDetailReport")
@ApiOperation("风险排查明细统计导出")
public void exportRiskDetailReport(DetailReportDto detailReportDto){
    //1.结合实际业务查询数据
        List<DetailReportViewDto> list = statisticsReportDomainService.riskDetailReport(premisesIdList, detailReportDto);
    //2.基于EasyExcel导出excel文件
        EasyExcelUtil.excelLockExport(DetailReportViewDto.class, "风险排查明细统计数据"+DateUtil.format(new Date(), "yyyyMMddHHmmssS"), list, null);
}

2.编写EasyExcel工具类

@Slf4j
public class EasyExcelUtil {
    //导出excel指定锁定列
    public static void excelLockExport(Class head, String excelname, List data, String sheetName) {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletResponse response = requestAttributes.getResponse();
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        try {
            // 这里URLEncoder.encode可以防止浏览器端导出excel文件名中文乱码 当然和easyexcel没有关系
            String fileName = URLEncoder.encode(excelname, "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
            EasyExcel.write(response.getOutputStream(), head)
                	//锁定工作簿
                    .registerWriteHandler(new LockSheetWriteHandler())
                	//指定单元格解锁
                    .registerWriteHandler(new CellHandler())
                    .sheet(sheetName == null ? "Sheet1" : sheetName).doWrite(data);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("导出数据失败: " + e.getMessage());
        }
    }
}

3.编写UnLockCell自定义注解

/**
 * 用于标记锁定哪些列不需要锁定
 */
@Target(value = {ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface UnLockCell {


}

使用@UnLockCell注解修饰不需要锁定的字段,这里每个字段都对应easy的表头

@HeadRowHeight(value = 15)
@ColumnWidth(value = 18)
@Data
@ApiModel(description = "风险排查明细报表DTO")
public class DetailReportViewDto extends DtoBase {

    @ColumnWidth(value = 0)
    @ExcelProperty(value = "id")
    @ApiModelProperty(value = "风险明细补充数据id")
    private Long id;

    @ColumnWidth(value = 0)
    @ExcelProperty(value = "账单明细id")
    @ApiModelProperty(value = "账单明细id")
    private Long noticeDetailId;

    @ColumnWidth(value = 25)
    @ExcelProperty(value = "欠费单位")
    @ApiModelProperty(value = "欠费单位")
    private String dataCustomerName;

    @ExcelProperty(value = "是否关联企业", converter = BooleanConvert.class)
    @ApiModelProperty(value = "是否关联企业")
    private Boolean relationEnterprise;

    @ExcelProperty(value = "客户属性")
    @ApiModelProperty(value = "客户属性:租户|业主")
    private String contractRoleType;

    @ExcelIgnore
    @ApiModelProperty(value = "所属事业部资源id")
    private Long orgResourceId;

    @ExcelProperty(value = "所属事业部")
    @ApiModelProperty(value = "所属事业部")
    private String orgName;

    @ExcelProperty(value = "项目部")
    @ApiModelProperty(value = "项目部")
    private String organization;

    @ExcelIgnore
    @ApiModelProperty(value = "项目部资源id")
    private Long organizationResourceId;

    @ExcelIgnore
    @ApiModelProperty(value = "楼盘资源id")
    private Long premisesId;

    @ColumnWidth(value = 25)
    @ExcelProperty(value = "楼盘")
    @ApiModelProperty(value = "楼盘", example = "楼盘1")
    private String premisesName;

    @ExcelProperty(value = "欠费类型")
    @ApiModelProperty(value = "欠费类型")
    private String arrearsType;

    @ExcelIgnore
    @ApiModelProperty(value = "费项id", hidden = true)
    private Long costItemId;

    @ExcelProperty(value = "欠费期间起期")
    @ApiModelProperty(value = "欠费期间起期")
    private Date costDateBegin;

    @ExcelProperty(value = "欠费期间止期")
    @ApiModelProperty(value = "欠费期间止期")
    private Date costDateEnd;

    @ExcelProperty(value = "欠费总金额", converter = MoneyConvert.class)
    @ApiModelProperty(value = "欠费总金额", example = "800000000")
    private Money arrearsAmount;

    //----------------导入后显示字段----------------
    @UnLockCell
    @ExcelProperty(value = "欠费原因")
    @ApiModelProperty(value = "欠费原因", example = "没钱")
    private String reasonSummary;

    @UnLockCell
    @ExcelProperty(value = "欠费可回收比例")
    @ApiModelProperty(value = "欠费可回收比例")
    private String recoverableRatio;

    @UnLockCell
    @ExcelProperty(value = "可收取金额预估", converter = MoneyConvert.class)
    @ApiModelProperty(value = "可收取金额预估")
    private Money estimateAmount;

    @UnLockCell
    @ExcelProperty(value = "不可收取金额预估", converter = MoneyConvert.class)
    @ApiModelProperty(value = "不可收取金额预估")
    private Money notChargeableAmount;

    @UnLockCell
    @ExcelProperty(value = "清收举措")
    @ApiModelProperty(value = "清收举措", example = "UrgeMeasuresEnum STOPMETERS")
    private String urgeMeasures;

    @UnLockCell
    @ExcelProperty(value = "后果预判")
    @ApiModelProperty(value = "后果预判")
    private String consequencePrediction;

    @UnLockCell
    @ExcelProperty(value = "解决建议")
    @ApiModelProperty(value = "解决建议")
    private String solutionSuggestion;

    @UnLockCell
    @ExcelProperty(value = "备注")
    @ApiModelProperty(value = "备注")
    private String remark;
}

4.编写WriteHandler

用于锁定工作簿

public class LockSheetWriteHandler implements SheetWriteHandler {

    @Override
    public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
    }

    @Override
    public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
        Sheet sheet = writeSheetHolder.getSheet();
        //锁定工作簿,设置保护密码
        sheet.protectSheet("1qaz!QAZ");
        // 锁定单元格不可选中(防止别人直接复制内容到其他excel修改)
        ((SXSSFSheet) writeSheetHolder.getSheet()).lockSelectLockedCells(true);
    }
}

用于指定单元格样式

public class CellHandler implements CellWriteHandler {

    private static final String PASSWORD = "1qaz!QAZ";

    /**
     * 在创建单元格之前调用
     * The maximum number of Cell Styles was exceeded. You can define up to 64000 style in a .xlsx Workbook
     *
     * @param writeSheetHolder
     * @param writeTableHolder
     * @param row
     * @param head
     * @param columnIndex
     * @param relativeRowIndex
     * @param isHead
     */
    @Override
    public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {
    }


    /**
     * 在创建单元格后调用
     *
     * @param writeSheetHolder
     * @param writeTableHolder
     * @param cell             * @param head
     * @param relativeRowIndex
     * @param isHead
     */
    @Override
    public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
    }

    /**
     * 在转换单元格数据后调用
     *
     * @param writeSheetHolder
     * @param writeTableHolder
     * @param cellData
     * @param cell
     * @param head
     * @param relativeRowIndex
     * @param isHead
     */
    @Override
    public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {

    }


    /**
     * 在完成对单元格的所有操作后调用
     *
     * @param writeSheetHolder
     * @param writeTableHolder
     * @param cellDataList
     * @param cell
     * @param head
     * @param relativeRowIndex
     * @param isHead
     */
    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
        //获取当前表头的字段名
        String fieldName = head.getFieldName();
        //获取到前面定义的DetailReportViewDto的Class对象
        Class clazz = writeSheetHolder.getClazz();
        //获取该类的所有声明的字段
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            //遍历判断字段名是否和表头的字段名相同
            if (field.getName().equals(fieldName)) {
                //相同则判断改字段是否被@UnLockCell注解修饰
                if (field.isAnnotationPresent(UnLockCell.class)) {
                    //被修饰则将单元格样式设置为不锁定
                    Map<String, Object> properties = new HashMap<>();
                    properties.put(CellUtil.LOCKED, false);
                    //这里使用CellUtil.setCellStyleProperties的方式
                    //对单元格设置样式不会影响原来的单元格可以实现单元格复用
                    CellUtil.setCellStyleProperties(cell, properties);
                }
            }
        }
    }
}

说明:这里分成两个writeHandler,一个用于锁定工作簿,一个用于指定单元格样式

我所猜的坑页正是在这里,最开始我是用的是CellHandler,在这个handler中去锁定工作簿,并指定锁定列遇到了如下问题:

The maximum number of Cell Styles was exceeded. You can define up to 64000 style in a .xlsx Workbook

错误的原因:创建了太多的单元格样式,而这个限制其实xlsx excel所限制的,而poi或者说easyexcel只是遵守规则。

起初查询了关于这个错误的很多文章,大多都是建议将创建样式的方法写在循环外面或者说是复用样式。

显然复用样式是比较靠谱的方案,我尝试了在CellHandler中进行复用,但在导出时会限于死循环,一直得不到响应。

解决办法:

  • 将锁定工作簿操作放在实现SheetWriteHandler接口的实现类中实现
  • 单元格样式复用操作,则在实现CellWriteHandler接口的实现类中实现

image-20221129190948467

3.最终效果

image-20221129193526121

image-20221129185658805

4.小总结

问题本身并不难,还是要多养成看API的习惯。有时候问题在网上找不到或者说应用场景不一样,看API文档确实会给到很大的帮助。

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

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

相关文章

AWS 中文入门开发教学 39- AWS CLI - AWS认证 必须会的命令行工具

AWS CLI是什么 AWS Command Line Interface (AWS CLI) 是一种开源工具&#xff0c; 让您能够在命令行 Shell 中使用命令与 AWS 服务进行交互。 仅需最少的配置&#xff0c;即可使用 AWS CLI 开始运行命令&#xff0c;以便从终端 程序中的命令提示符实现与基于浏览器的 AWS 管理…

mysql基础知识篇(四)

1.MySQL 索引用的什么数据结构了解吗&#xff1f; MySQL 的默认存储引擎是 InnoDB&#xff0c;它采用的是 B树结构的索引。 B树&#xff1a;只有叶子节点才会存储数据&#xff0c;非叶子节点只存储键值。叶子节点之间使用双向指针连接&#xff0c;最底层的叶子节点形成了一个…

FTP服务配置和使用

FTP介绍 FTP&#xff08;文件传输协议20、21端口&#xff09;是典型的C/S架构的应用层协议&#xff0c;需要由服务端软件、客户端软件两个部分共同实现文件传输协议。FTP是客户端和服务器之间的连接是可靠的保证。 FTP是一种文件传输协议&#xff0c;它支持两种模式&#xff…

集合框架----源码解读LikedHashSet篇

1.官方介绍 Hash表和链表实现了Set接口&#xff0c;具有可预测的迭代顺序。该实现与HashSet的不同之处在于它维护了一个贯穿其所有条目的双向链表。该链表定义了迭代顺序&#xff0c;即元素插入集合的顺序(插入顺序)。注意&#xff0c;如果一个元素重新插入到集合中&#xff0c…

Git---idea中git的基本操作

idea中使用git仓库 idea中配置git仓库&#xff1a; 首先idea配置git仓库的位置 配置完成之后&#xff0c;有两种创建仓库的方式 从本地配置git仓库&#xff1a; idea本身设置好的&#xff0c;直接下一步就好 从远程克隆仓库&#xff1a; 如果远程仓库没有的话可以绑定完…

如何从 FastReport VCL 中将报表导出为PNG格式?

FastReport VCL是用于在软件中集成商务智能的现代解决方案。它提供了可视化模板设计器&#xff0c;可以访问最受欢迎的数据源&#xff0c;报告引擎&#xff0c;预览&#xff0c;将过滤器导出为30多种格式&#xff0c;并可以部署到云&#xff0c;Web&#xff0c;电子邮件和打印中…

openvswitch group hash实现代码分析

代码分析 ovs版本是2.11.0&#xff0c;linux版本是linux-3.10.0-693.21.1.el7。 只拿ovs实现的group hash和dp_hash举例分析代码&#xff0c;通过一个点一个功能切入代码&#xff0c;漫无目的看代码是很难看懂的&#xff0c;必须带着一个疑问看代码&#xff0c;点多了全面开花…

降低点云密度的几种方法(含python代码)

本文只是对学习过程中的点云密度降采样的几种方法做一个记录&#xff0c;原文参考知乎Python点云数据处理(四)点云下采样 - 知乎 (zhihu.com) 本文介绍python点云数据处理中的点云下采样算法和关键点算法以及在点云工具箱软件中的实现。由于点云的海量和无序性&#xff0c;直接…

Java基于springboot+vue的房屋出租租房系统 前后端分离

伴随着全球信息化发展&#xff0c;行行业业都与计算机技术相衔接&#xff0c;计算机技术普遍运用于各大行业&#xff0c;房屋出租管理系统便是其中一种。实施计算机系统来管理可以降低大学生租房管理的成本&#xff0c;使整个大学生租房的发展和服务水平有显著提升。 本论文主要…

umask 设置文件权限掩码

我们在创建文件或者目录时&#xff0c;看到的权限往往和我们设置的不一样&#xff0c;原因就在于创建文件时要受到 umask的影响。 目录 一、实际情景介绍 二、文件权限掩码 1、什么是权限掩码&#xff1f; 2、权限掩码的作用过程 3、设置权限掩码的两种方式 (1) umask 命…

【java基础系列】14- Java的内部类与常用类

Java的内部类与常用类 1、内部类 1.1 内部类的分类 成员内部类静态内部类局部内部类匿名内部类 1.2 什么是内部类&#xff1f; 概念&#xff1a;在一个类的内部再定义一个完整的类。特点&#xff1a; 编译之后可生成独立的字节码文件。内部类可直接访问外部类的私有成员&a…

【HIT-OSLAB-实验报告】

文章目录前言实验 0 环境的搭建实验原理&材料实验流程建议实验1 输出硬件参数实验内容基础知识实验代码实验结果实验2 实现系统调用实验内容whoami()评分标准基础知识实验代码实验结果实验3 进程运行轨迹的跟踪实验内容基础知识实验代码实验结果结合自己的体会 从程序设计者…

Elasticsearch

一、Spring Data 1、简介 Spring Data 是一个用于简化数据库、非关系型数据库、索引库访问&#xff0c;并支持云服务的开源框架。Spring Data 可以极大的简化JPA的写法&#xff0c;可以在几乎不用写实现的情况下&#xff0c;实现对数据库的访问和操作。除了 CRUD 之外&#xff…

作业-11.29

将txt中的单词转到数据库中 #include <stdio.h> #include <sqlite3.h> #include <stdlib.h> #include <string.h> void do_insert(sqlite3* db, int id, char word[], char jieshi[]); void txt_todatabase(sqlite3* db); int main(int argc, const ch…

DevExpress FMX Data Grid全面编辑和定制

DevExpress FMX Data Grid全面编辑和定制 FMX数据网格(CTP)FireMonkey(FMX)的高性能数据网格组件&#xff0c;具有集成的主细节和数据分组支持。它被优化并构建为与RAD Studio/Delphi/CBuilder一起使用。它支持Windows、Android和macOS平台。 DevExpress FMX数据网格功能强大&a…

redis介绍和理解

官网 介绍: https://www.bilibili.com/video/BV1Fd4y1T7pD/?spm_id_from333.337.search-card.all.click&vd_source4c263677a216945c0d21ca65ee15a5f9 Redis是一个key value的数据库&#xff0c;基于内存、分布式、可选持久性的键值对(Key-Value)存储数据库。 https://ww…

【Java+LeetCode训练】binarySearch源码解析

二分搜索Arrays.binarySearch(int[] a,int key)源码分析【LeetCode】209. 长度最小的子数组解法1&#xff1a;前缀和 暴力解法解法2&#xff1a;前缀和 二分搜索序&#xff1a;使用Arrays工具类中的binarySearch方法进行二分搜索时&#xff0c;我们知道搜索成功会返回其下标&…

数字化餐饮| 刘大厨湘菜馆进杭州,开场及巅峰

盼了几年的刘大厨辣椒炒肉终于来杭州了&#xff0c;但我却没有吃到&#xff0c;小钱对雨科网说&#xff1a;驱车三十里&#xff0c;排队三小时都没吃上&#xff0c;原来他们是每天10点开始放号&#xff0c;11点开餐&#xff0c;去的晚就吃不到。 5月20日&#xff0c;刘大厨在杭…

5G无线技术基础自学系列 | 5G上行物理信道和信号

素材来源&#xff1a;《5G无线网络规划与优化》 一边学习一边整理内容&#xff0c;并与大家分享&#xff0c;侵权即删&#xff0c;谢谢支持&#xff01; 附上汇总贴&#xff1a;5G无线技术基础自学系列 | 汇总_COCOgsta的博客-CSDN博客 5G上行的物理信道包括PRACH、PUCCH、PU…

产品经理要不要考PMP?进化你能力的阶梯!(附:新版考纲及教材)

产品经理和项目经理看起来是毫不相关的两个专业&#xff0c;那么产品经理要不要考PMP呢&#xff1f;其实是非常有必要的。 以前去面试产品经理&#xff0c;HR只会问1个问题&#xff1a;会用axure吗&#xff1f;一开始对产品经理的定义就是设计产品原型的。能设计产品原型&…