easyexcel 2.2.6版本导出excel模板时,标题带下拉框及其下拉值过多不显示问题

news2025/2/22 21:19:11

需求背景:有一个需求要做下拉框的值有100多条,同时这个excel是一个多sheet的导入模板
直接用easyexcel 导出,会出现下拉框的值过多,导致生成出来的excel模板无法正常展示下拉功能

 

使用的easyexcel版本:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>2.2.6</version>
</dependency>
自定义处理器

package com.manager.utils;

import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddressList;
import java.util.Map;

/**
 * 解决使用 easyExcel导出模板,下拉框数据超长,导出模板后,下拉框数据不展示问题
 * @author yjj
 * @date 2025/02/18 10:16
 **/
public class EasyExcelCellWriteHandler implements SheetWriteHandler {

    public static final String SHEET_NAME = "下拉框隐藏表hidden";

    /**
     * 设置阈值,避免生成的导入模板下拉值获取不到
     */
    private static final Integer LIMIT_NUMBER = 50;

    private Map<Integer, String[]> map = null;

    public EasyExcelCellWriteHandler(Map<Integer, String[]> map) {
        this.map = map;
    }

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

    }


    @Override
    public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
        // 这里可以对cell进行任何操作
        Sheet sheet = writeSheetHolder.getSheet();
        DataValidationHelper helper = sheet.getDataValidationHelper();
        Class<?> headClass = writeSheetHolder.getClazz();

        // k 为存在下拉数据集的单元格下表 v为下拉数据集
        map.forEach((k, v) -> {
            System.out.println("Key = " + k + ", Value = " + v);
            // 设置下拉单元格的首行 末行 首列 末列
            CellRangeAddressList rangeList = new CellRangeAddressList(EasyExcelUtils.headRowNumber(headClass), 65536,k,k);
            // 如果下拉值总数大于50,则使用一个新sheet存储,避免生成的导入模板下拉值获取不到
            if (v.length > LIMIT_NUMBER) {
                //定义sheet的名称
                //1.创建一个隐藏的sheet 名称为 hidden + k
                String sheetName = SHEET_NAME +sheet.getSheetName() + k;
                Workbook workbook = writeWorkbookHolder.getWorkbook();
                Sheet hiddenSheet = workbook.createSheet(sheetName);
                for (int i = 0, length = v.length; i < length; i++) {
                    // 开始的行数i,列数k
                    hiddenSheet.createRow(i).createCell(k).setCellValue(v[i]);
                }
                Name category1Name = workbook.createName();
                category1Name.setNameName(sheetName);
                String excelLine = getExcelLine(k);
                // =hidden!$H:$1:$H$50  sheet为hidden的 H1列开始H50行数据获取下拉数组
                String refers = "=" + sheetName + "!$" + excelLine + "$1:$" + excelLine + "$" + (v.length + 1);
                // 将刚才设置的sheet引用到你的下拉列表中
                DataValidationConstraint constraint = helper.createFormulaListConstraint(refers);
                DataValidation dataValidation = helper.createValidation(constraint, rangeList);
                writeSheetHolder.getSheet().addValidationData(dataValidation);
                // 设置存储下拉列值得sheet为隐藏
                int hiddenIndex = workbook.getSheetIndex(sheetName);
                if (!workbook.isSheetHidden(hiddenIndex)) {
                    workbook.setSheetHidden(hiddenIndex, true);
                }
            }
            // 下拉列表约束数据
            DataValidationConstraint constraint = helper.createExplicitListConstraint(v);
            // 设置约束
            DataValidation validation = helper.createValidation(constraint, rangeList);
            // 阻止输入非下拉选项的值
            validation.setErrorStyle(DataValidation.ErrorStyle.STOP);
            validation.setShowErrorBox(true);
            validation.setSuppressDropDownArrow(true);
            validation.createErrorBox("提示", "此值与单元格定义格式不一致");
            // validation.createPromptBox("填写说明:","填写内容只能为下拉数据集中的单位,其他单位将会导致无法入仓");
            sheet.addValidationData(validation);
        });
    }

    /**
     * 返回excel列标A-Z-AA-ZZ
     *
     * @param num 列数
     * @return java.lang.String
     */
    private String getExcelLine(int num) {
        String line = "";
        int first = num / 26;
        int second = num % 26;
        if (first > 0) {
            line = (char) ('A' + first - 1) + "";
        }
        line += (char) ('A' + second) + "";
        return line;
    }

}
导出工具类

/**
     * 支持超长下拉框展示
     * 下载导入模板 - 支持多sheet
     */
    public static void writeTemplateBoxTooLong(HttpServletResponse response, ExcelModel excelModel) {

        ExcelWriter excelWriter = null;
        try {
            excelWriter = EasyExcel.write(outputStream(excelModel.getFileName(), response))
                    .registerConverter(new DateConverter())
                    .useDefaultStyle(false)
                    .build();

            List<ExcelModel.Sheet<?>> sheets = excelModel.getSheets();

            for (int i = 0; i < sheets.size(); i++) {

                ExcelModel.Sheet<?> sheet = sheets.get(i);

                WriteSheet writeSheet;
                if (ExtraOption.class.isAssignableFrom(sheet.getHeadClass())) {

                    writeSheet = EasyExcel.writerSheet(i, sheet.getSheetName())
                            .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                            .registerWriteHandler(HorizontalCellStyleStrategyFactory.optStyleStrategy())
                            .head(sheet.getHeadClass())
                            .sheetName(sheet.getSheetName())
                            .build();

                } else {
                    Map<Integer, String[]> map = buildExcelDropDownSetField(sheet.getHeadClass());
                    writeSheet = EasyExcel.writerSheet(i, sheet.getSheetName())
                            .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                            .registerWriteHandler(new EasyExcelCellWriteHandler(map))
                            .registerWriteHandler(new ImportTempleRowWriteHandler())
                            .registerWriteHandler(new ImportTempleCellWriteHandler())
                            .head(sheet.getHeadClass())
                            .sheetName(sheet.getSheetName())
                            .relativeHeadRowIndex(RELATIVE_HEAD_ROW_INDEX)
                            .includeColumnFiledNames(sheet.getIncludeFiledNames())
                            .build();
                }

                excelWriter.write(sheet.getData(), writeSheet);
            }
        } catch (Exception e) {

            Throwable cause = Throwables.getRootCause(e);
            log.error("下载模板失败:{}", cause.getMessage(), cause);
            throw new ServiceException("下载模板失败:{0}", cause.getMessage());
        } finally {
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }

    }
 /**
     * 批量导入 - 支持多sheet
     */
    @SuppressWarnings("rawtypes")
    public static Map<Class<?>, List<?>> readMultiSheet(MultipartFile file, Class<?>... classes) {


        Map<Class<?>, List<?>> resultMap = Maps.newHashMapWithExpectedSize(classes.length + 1);
        try {
            checkExcelFile(file);
            ByteArrayInputStream inputStream = deleteHiddenSheets(file);
            ExcelReader excelReader = EasyExcel.read(inputStream).build();
            for (int i = 0; i < classes.length; i++) {

                Class clazz = classes[i];
                SimpleAnalysisEventListener listener = SimpleAnalysisEventListener.factory(true);

                ReadSheet readSheet = EasyExcel.readSheet(i)
                        .head(clazz)
                        .registerReadListener(listener)
                        .headRowNumber(headRowNumber(clazz))
                        .build();

                excelReader.read(readSheet);

                resultMap.put(clazz, listener.getResults());
            }

            if (resultMap.values().stream().allMatch(CollectionUtils::isEmpty)) {
                throw new ServiceException("请至少录入一条数据");
            }
        } catch (Exception e) {

            Throwable cause = Throwables.getRootCause(e);
            log.error("解析异常:{}", cause.getMessage(), cause);
            throw new ServiceException("解析异常:{0}", cause.getMessage());
        }
        return resultMap;
    }


    //删除导出模板时生成的隐藏sheet,避免导入时读取带隐藏sheet报错
    public static ByteArrayInputStream deleteHiddenSheets(MultipartFile file){
        try (InputStream inputStream = file.getInputStream();
             ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
             ){
            Workbook workbook = new XSSFWorkbook(inputStream);
            for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
                Sheet sheet = workbook.getSheetAt(i);
                if (sheet.getSheetName().contains(EasyExcelCellWriteHandler.SHEET_NAME)) {
                    workbook.removeSheetAt(i);
                    i--;  // 因为删除了一个sheet,索引需要调整
                }
            }
            workbook.write(outputStream);
            workbook.close();
            return new ByteArrayInputStream(outputStream.toByteArray());
        } catch (IOException e) {
            log.error("解析excel失败!",e);
            throw new ServiceException("解析失败!");
        }
    }

原生poi参考这位大佬:解决POI的SXSSFSheet 创建excel下拉框,下拉框内容过多时不显示的问题_java poi 下拉框数据7万行,隐藏sheet方法也看不不全-CSDN博客

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

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

相关文章

影视大数据分析新范式:亮数据动态代理驱动的实时数据采集方案

一、项目背景与挑战 在数据驱动决策的时代&#xff0c;影视数据分析对内容平台至关重要。但豆瓣等平台设有&#xff1a; 高频请求IP封禁机制User-Agent指纹检测请求频率阈值控制验证码验证系统 传统爬虫方案面临&#xff1a; 单一IP存活时间<5分钟采集成功率<30%数据更新…

免费体验,在阿里云平台零门槛调用满血版DeepSeek-R1模型

一、引言 随着人工智能技术的飞速发展&#xff0c;各类AI模型层出不穷。其中&#xff0c;DeepSeek作为一款新兴的推理模型&#xff0c;凭借其强大的技术实力和广泛的应用场景&#xff0c;逐渐在市场中崭露头角。本文将基于阿里云提供的零门槛解决方案&#xff0c;对DeepSeek模…

Cursor 与团队协作:提升团队开发效率

引言 在团队开发中&#xff0c;代码质量参差不齐、重复错误频发、代码审查耗时过长是制约效率的三大痛点。据 GitHub 调查&#xff0c;开发者平均每周花费 4.3 小时修复他人代码问题&#xff0c;而 60% 的合并请求&#xff08;PR&#xff09;因风格或低级错误被驳回。Cursor 作…

激光工控机在自动化生产线中有什么关键作用?

激光工控机作为自动化生产线的核心设备&#xff0c;通过高精度控制、快速响应和智能化集成&#xff0c;在提升效率、保障质量、实现柔性制造等方面发挥着不可替代的作用。以下是其关键作用的具体分析&#xff1a; 一、实现高效连续生产&#xff1a; 1.高速加工能力&#xff1…

深度解析应用层协议-----HTTP与MQTT(涵盖Paho库)

HTTP协议概述 1.1 HTTP的基本概念 HTTP是一种应用层协议&#xff0c;使用TCP作为传输层协议&#xff0c;默认端口是80&#xff0c;基于请求和响应的方式&#xff0c;即客户端发起请求&#xff0c;服务器响应请求并返回数据&#xff08;HTML&#xff0c;JSON&#xff09;。在H…

WordPress“更新失败,响应不是有效的JSON响应”问题的修复

在使用WordPress搭建网站时&#xff0c;许多人在编辑或更新文章时&#xff0c;可能会遇到一个提示框&#xff0c;显示“更新失败&#xff0c;响应不是有效的JSON响应”。这个提示信息对于不了解技术细节的用户来说&#xff0c;太难懂。其实&#xff0c;这个问题并不复杂&#x…

【开源】思维导图:思绪思维导图 (Simple Mind Map)

目录 1. 思绪思维导图 2. 本地使用 3. 在线网页 1. 思绪思维导图 思绪思维导图&#xff0c;英文名称为Simple mind map&#xff0c;简称为SSM。 开源、免费 GitHub网址 支持多平台&#xff1a;Windows&#xff0c;Mac和Linux 支持在线和离线使用 2. 本地使用 安装包下载地址…

国产编辑器EverEdit -告别东找西找!一键打开当前文件所在目录!

1 文件操作 2 应用场景 在文件编辑过程中&#xff0c;有时需要对文件进行一些操作&#xff0c;比如&#xff1a;在命令窗口输入文件路径、文件名&#xff0c;进入到文件目录&#xff0c;对文件进行压缩等&#xff0c;如果没有直达命令&#xff0c;用户需要通过文件管理器找到目…

UE引擎游戏加固方案解析

据VGinsights的报告&#xff0c;近年来UE引擎在过去几年中市场占比显著增长&#xff0c;其中亚洲市场增幅达到了30%&#xff0c;随着UE5的推出和技术的不断进步&#xff0c;UE引擎在独立开发者和移动游戏开发中的应用也在逐步增加。 UE引擎的优势在于强大的画面表现与视觉特效…

[kubelet-check] It seems like the kubelet isn‘t running or healthy.

执行k8s时报错&#xff1a; [kubelet-check] It seems like the kubelet isn’t running or healthy. [kubelet-check] The HTTP call equal to ‘curl -sSL http://localhost:10248/healthz’ failed with error: Get "http://localhost:10248/heal ** 解决办法如下&a…

C# 背景 透明 抗锯齿 (效果完美)

主要是通过 P/Invoke 技术调用 Windows API 函数 gdi32.dll/user32.dll&#xff0c;同时定义了一些结构体来配合这些 API 函数的使用&#xff0c;常用于处理图形绘制、窗口显示等操作。 运行查看效果 局部放大&#xff0c;抗锯齿效果很不错,尾巴毛毛清晰可见。 using System; u…

关于uniApp的面试题及其答案解析

我的血液里流淌着战意&#xff01;力量与智慧指引着我&#xff01; 文章目录 1. 什么是uniApp&#xff1f;2. uniApp与原生小程序开发有什么区别&#xff1f;3. 如何使用uniApp实现条件编译&#xff1f;4. uniApp支持哪些平台&#xff0c;各有什么特点&#xff1f;5. 在uniApp中…

【Java场景题】MySQL死锁排查

大家好&#xff0c;今天XiXi给大家分享一个MySQL死锁排查的实验&#xff0c;文章主要有&#xff1a; 通过show engine innodb status&#xff0c;查看最近一次死锁信息开启innodb_print_all_deadlocks&#xff0c;在错误日志中能够记录所有死锁信息通过解析binlog日志定位死锁…

LabVIEW心音信号采集与分析系统

基于LabVIEW软件的心音信号采集与分析系统能够实现心音的采集、去噪和分析。系统利用LabVIEW的强大功能和灵活性&#xff0c;通过模块化设计&#xff0c;实现了心音信号的高效处理和分析&#xff0c;具备深度学习和身份识别的实验能力&#xff0c;适用于医学和生物工程领域的研…

【Scrapy】Scrapy教程7——存储数据

上一节我们对爬虫程序的默认回调函数parse做了改写,提取的数据可以在Scrapy的日志中打印出来了,光打印肯定是不行的,还需要把数据存储,数据可以存到文件,也可以存到数据库,我们一一来看。 存储数据到文件 首先我们看看如何将数据存储到文件,在讲[[【Scrapy】Scrapy教程…

基础入门-算法解密散列对称非对称字典碰撞前后端逆向MD5AESDESRSA

知识点&#xff1a; 0、算法类型-单向散列&对称性&非对称性 1、算法识别加解密-MD5&AES&DES&RSA 2、解密条件寻找-逻辑特征&源码中&JS分析 应用场景&#xff1a; 1、发送数据的时候自动将数据加密发送&#xff08;只需加密即可&#xff09; 安全…

在UBUNTU下搭建Deepseek

在UBUNTU下搭建Deepseek 一、安装UBUNTU 这个就不多说了&#xff0c;无外乎下载UBUNTU的iso&#xff0c;然后用UltraIso制作U盘&#xff0c;然后重启设置启动盘&#xff0c;安装… 二、安装Ollama curl -sSfL https://ollama.com/install.sh | sh这里可能需要你先安装curl工…

O1 Embedder:让检索器思考后再行动

25年2月来自中科大和北京智源研究院的论文“O1 Embedder: Let Retrievers Think Before Action”。 大语言模型 (LLM) 的功能日益强大&#xff0c;彻底改变人们获取和利用信息的方式。值得注意的是&#xff0c;LLM 擅长执行细粒度数据表示&#xff0c;这有助于精确检索信息。它…

Ubuntu系统3分钟本地部署DeepSeek-R1蒸馏模型,支持联网

本文提供Ubuntu ollama Page Assist&#xff0c;3步快速安装DeepSeek-R1蒸馏模型&#xff0c;支持联网&#xff0c;支持API。 目录 DeepSeek-R1安装分3步&#xff1a; Step 1, 安装ollama&#xff08;已安装可忽略&#xff09; Step 2, 下载DeepSeek-R1模型 Step 3, 从…

谷粒商城—分布式高级②.md

认证服务 1. 环境搭建 创建gulimall-auth-server模块,导依赖,引入login.html和reg.html,并把静态资源放到nginx的static目录下 2. 注册功能 (1) 验证码倒计时 //点击发送验证码按钮触发下面函数 $("#sendCode").click(function () {//如果有disabled,说明最近…