Excel设置单元格下拉框(poi)
前言
年关在即,还在最后的迭代处理,还分了个其他同事的单,说是导出的Excel模版的2列要修改为下拉选项,过程很曲折,不说,以下其实就是一个笔记而已!
其实之前分享过阿里的EasyExcel设置单元格下拉框,这里是poi原生设置。
一、场景描述
其实就是下载一个Excel文件,作为导入的模版,然后有一行示例,2列是字典值,希望给用户的输入做个示例。
二、使用步骤
1.引入库
主要就是引入poi相关包
<dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>4.1.0</version>
</dependency>
<dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>4.1.0</version>
</dependency>
2.工具类
这里分享工具类的部分方法,免得看的眼花缭乱。
ExcelTools
/*** 导出带下拉框的Excel** @param outStream 输出流* @param keyValueMap 表头和属性的Map集合,其中Map中Key为Excel列的名称,Value为反射类的属性* @param list 数据集合* @param fileType 文件类型* @param title 表头* @param dropDownMap 下拉框Map集合,其中Map中Key为Excel列的序号,Value为下拉框的值* @param <T> 泛型* @throws Exception 异常* @auther zhengwen*/public static <T> void exportDropDownExcel(OutputStream outStream, Map<String, String> keyValueMap, List<T> list,String fileType, String title, Map<Integer, List<String>> dropDownMap) throws Exception {Workbook wb = createWorkbook(fileType);exportExcel(wb, outStream, keyValueMap, list, fileType, title, dropDownMap);}/*** 根据文件后缀创建对应的 Workbook* @param fileType 文件后缀:xls,xlsx* @return Workbook*/private static Workbook createWorkbook(String fileType){return ExcelConstant.XLS.equals(fileType) ? new HSSFWorkbook() : new XSSFWorkbook();}/*** 创建带下拉选项的Workbook** @param wb Workbook* @param outStream 输出流* @param keyValueMap 表头和属性的Map集合,其中Map中Key为Excel列的名称,Value为反射类的属性* @param list 数据集合* @param fileType 文件类型* @param title 表头* @param dropDownMap 下拉框Map集合,其中Map中Key为Excel列的序号,Value为下拉框的值* @param <T> 泛型* @throws Exception 异常* @auther zhengwen*/private static <T> void exportExcel(Workbook wb, OutputStream outStream, Map<String, String> keyValueMap, List<T> list, String fileType, String title, Map<Integer, List<String>> dropDownMap) throws Exception {List<String> keyList = new ArrayList<>(keyValueMap.keySet());// 存储属性信息Map<String, String> attMap = new HashMap<>(CommonConstant.MAP_DEFAULT_INITIAL_CAPACITY);int index = CommonConstant.ZERO;if (ExcelConstant.XLS.equals(fileType)) {// 创建HSSFWorkbook对象(excel的文档对象)CellStyle cellStyle = wb.createCellStyle();cellStyle.setWrapText(true);cellStyle.setAlignment(HorizontalAlignment.CENTER);cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);int lastCol = keyValueMap.size() - 1;CellRangeAddress callRangeAddress = new CellRangeAddress(0, 0, 0, lastCol);// 建立新的sheet对象(excel的表单)HSSFSheet sheet = (HSSFSheet) wb.createSheet(ExcelConstant.SHEET_NAME);// 设置表格默认列宽度为15个字节sheet.setDefaultColumnWidth(20);sheet.addMergedRegion(callRangeAddress);// 声明样式HSSFCellStyle style = (HSSFCellStyle) wb.createCellStyle();hssfCellStyleForXls(wb, style);//表格标题HSSFRow rowTitle = sheet.createRow(CommonConstant.ZERO);HSSFCell titleCell = rowTitle.createCell(0);rowTitle.setHeight((short) 600);//加载单元格样式CellStyle erStyle = createCellStyle(wb, (short) 13, true, true);titleCell.setCellStyle(erStyle);titleCell.setCellValue(title);//标题换行设置erStyle.setWrapText(true);titleCell.setCellValue(new HSSFRichTextString(title));// 在sheet里创建第二行为表头,参数为行索引(excel的行),可以是0~65535之间的任何一个HSSFRow rowHeader = sheet.createRow(CommonConstant.ONE);DataFormat dataFormat = wb.createDataFormat();for (String key : keyList) {HSSFCell cell = rowHeader.createCell(index);style.setWrapText(true);//设置为文本,防止科学计数style.setDataFormat(dataFormat.getFormat("@"));cell.setCellStyle(style);cell.setCellValue(key);attMap.put(Integer.toString(index), keyValueMap.get(key));index++;}// 在sheet里创建表头下的数据for (int i = CommonConstant.ZERO; i < list.size(); i++) {int rowNum = i + CommonConstant.TWO;HSSFRow row = sheet.createRow(rowNum);for (int j = CommonConstant.ZERO; j < keyValueMap.size(); j++) {String key = Integer.toString(j);String att = attMap.get(key);T t = list.get(i);Object value = getAttrVal(t, att);cellStyle.setWrapText(true);Cell cell = row.createCell(j);cell.setCellStyle(cellStyle);if (dropDownMap.containsKey(j)) {setCellDropDownBox(cell, dropDownMap.get(j), sheet, rowNum, rowNum, j, j);} else {cell.setCellValue(getObject(value));}}}} else if (ExcelConstant.XLSX.equals(fileType)) {int lastCol = keyValueMap.size() - 1;CellRangeAddress callRangeAddress = new CellRangeAddress(0, 0, 0, lastCol);// 建立新的sheet对象(excel的表单)XSSFSheet sheet = (XSSFSheet) wb.createSheet(ExcelConstant.SHEET_NAME);// 设置表格默认列宽度为15个字节sheet.setDefaultColumnWidth(20);sheet.addMergedRegion(callRangeAddress);// 声明样式XSSFCellStyle style = (XSSFCellStyle) wb.createCellStyle();xssfCellStyleFor2007(wb, style);//标题XSSFRow rowTitle = sheet.createRow(CommonConstant.ZERO);rowTitle.setHeight((short) 600);//表格标题XSSFCell titleCell = rowTitle.createCell(0);//加载单元格样式CellStyle erStyle = createCellStyle(wb, (short) 13, true, true);titleCell.setCellStyle(erStyle);titleCell.setCellValue(title);//标题换行设置erStyle.setWrapText(true);titleCell.setCellValue(new XSSFRichTextString(title));// 在sheet里创建第一行为表头,参数为行索引(excel的行),可以是0~65535之间的任何一个XSSFRow rowHeader = sheet.createRow(CommonConstant.ONE);// 创建单元格并设置单元格内容for (String key : keyList) {XSSFCell cell = rowHeader.createCell(index);style.setWrapText(true);cell.setCellStyle(style);cell.setCellValue(key);attMap.put(Integer.toString(index), keyValueMap.get(key));index++;}// 在sheet里创建表头下的数据for (int i = CommonConstant.ZERO; i < list.size(); i++) {int rowNum = i + CommonConstant.TWO;XSSFRow row = sheet.createRow(rowNum);for (int j = CommonConstant.ZERO; j < keyValueMap.size(); j++) {String key = Integer.toString(j);T t = list.get(i);Object value = getAttrVal(t, attMap.get(key));CellStyle cellStyle = wb.createCellStyle();cellStyle.setWrapText(true);cellStyle.setAlignment(HorizontalAlignment.CENTER);cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);// 下边框cellStyle.setBorderBottom(BorderStyle.THIN);// 左边框cellStyle.setBorderLeft(BorderStyle.THIN);// 上边框cellStyle.setBorderTop(BorderStyle.THIN);// 右边框cellStyle.setBorderRight(BorderStyle.THIN);Font cellfont = wb.createFont();cellfont.setFontHeightInPoints((short) 12);cellfont.setFontName("宋体");cellStyle.setFont(cellfont);//row.createCell(j).setCellValue(getObject(value));//row.createCell(j).setCellStyle(cellStyle);//以上2行写的害人的,会导致单元格格式丢失DataFormat format = wb.createDataFormat();cellStyle.setDataFormat(format.getFormat("@"));//sheet.setDefaultColumnStyle(j, cellStyle);Cell cell = row.createCell(j);cell.setCellStyle(cellStyle);if (dropDownMap.containsKey(j)) {setCellDropDownBox(cell, dropDownMap.get(j), sheet, rowNum, rowNum, j, j);} else {cell.setCellValue(getObject(value));}}}}if (null != wb) {wb.write(outStream);}}/*** 设置cell单元格的下拉框** @param cell 单元格* @param dropDownMap 下拉框Map集合,其中Map中Key为Excel列的序号,Value为下拉框的值* @param sheet sheet* @param startRow 开始行号* @param endRow 结束行号* @param startCol 开始列号* @param endCol 结束列号* @auther zhengwen*/private static void setCellDropDownBox(Cell cell, List<String> dropDownMap, Sheet sheet, int startRow, int endRow, int startCol, int endCol) {if (cell == null || sheet == null) {throw new DreambaseException("单元格或者sheet为空");}// 生成下拉框内容String[] dropDownArray = dropDownMap.toArray(new String[0]);//设置下拉框CellRangeAddressList regions = new CellRangeAddressList(startRow, endRow, startCol, endCol);if (sheet instanceof XSSFSheet){XSSFSheet xssfSheet = (XSSFSheet) sheet;// 生成下拉框DataValidationHelper helper = sheet.getDataValidationHelper();DataValidationConstraint constraint = helper.createExplicitListConstraint(dropDownArray);DataValidation dataValidation = helper.createValidation(constraint, regions);xssfSheet.addValidationData(dataValidation);}if (sheet instanceof HSSFSheet){// 生成下拉框DVConstraint constraint = DVConstraint.createExplicitListConstraint(dropDownArray);// 绑定下拉框和作用区域HSSFDataValidation dataValidation = new HSSFDataValidation(regions, constraint);sheet.addValidationData(dataValidation);}//设置下拉框选中第一个cell.setCellValue(dropDownArray[0]);}
serviceImpl
@Overridepublic void exportExcel(List<StationExcel> excelList, HttpServletRequest request, HttpServletResponse response){try {OutputStream outputStream = OutputStreamUtil.getOutputStream(request, response, StationExcel.FILE_NAME);//下拉框数据Map<Integer,List<String>> dropDownMap = new HashMap<>();//查询站点类型List<Dictionary> stationTypeDictList = dictionaryService.queryDictionaryByCategory(CommonConstant.DICT_STATION_TYPE);if(CollectionUtil.isNotEmpty(stationTypeDictList)){List<String> typeList = new ArrayList<>();stationTypeDictList.forEach((e) -> typeList.add(e.getDesc()));dropDownMap.put(1,typeList);}//查询电压等级List<Dictionary> electricPowerLevelDictList = dictionaryService.queryDictionaryByCategory(CommonConstant.DICT_ELECTRIC_POWER_LEVEL_TYPE);if(CollectionUtil.isNotEmpty(electricPowerLevelDictList)){List<String> electricPowerLevelList = new ArrayList<>();electricPowerLevelDictList.forEach((e) -> electricPowerLevelList.add(e.getDesc()));dropDownMap.put(3,electricPowerLevelList);}ExcelTools.exportDropDownExcel(outputStream, StationExcel.COLUMN_LIST, excelList, ExcelConstant.XLSX, StationExcel.TITLE, dropDownMap);response.flushBuffer();outputStream.close();} catch (IOException e) {log.error("excel导出失败", e);throw new DreambaseException("excel导出失败!IOException异常" + e.getMessage());} catch (Exception e) {log.error("excel导出失败", e);throw new DreambaseException("excel导出失败!" + e.getMessage());}}
总结
其实没啥好分享的,真的就是个笔记而已,希望可以帮到大家!
相关文章:
Excel设置单元格下拉框(poi)
前言 年关在即,还在最后的迭代处理,还分了个其他同事的单,说是导出的Excel模版的2列要修改为下拉选项,过程很曲折,不说,以下其实就是一个笔记而已! 其实之前分享过阿里的EasyExcel设置单…...
api接口是什么意思,api接口该如何防护呢?
API接口:应用程序与服务之间的接口 什么是API接口 API是应用程序接口的缩写,指的是能够让不同的应用程序之间交换数据的一种方式。一个API接口就是应用程序与服务之间的接口,它定义了服务提供的功能和数据,以及应用程序如何访问这…...

PMP资料怎么学?PMP备考经验分享
PMP考试前大家大多都是提前备考个一两个月,但是有些朋友喜欢“不走寻常路”,并不打算去考PMP认证,想要单纯了解PMP,不管要不要考证,即使是仅仅学习了解一下我个人都非常支持,因为专业的基础的确能提高工作效…...
partition by list(msn_id)子句的含义
在数据库查询中,特别是在使用SQL语言时,"PARTITION BY" 子句用于对结果集进行分区,以便可以对每个分区进行单独的聚合操作。这是在执行窗口函数(如 ROW_NUMBER(), RANK(), SUM(), AVG() 等)时特别有用的。 …...

【C++】I/O多路转接详解(二)
在上一篇文章【C】I/O多路转接详解(一) 在出现EPOLL之后,随之而来的是两种事件处理模式的应运而生:Reator 和 Proactor,同步IO模型常用于Reactor模式,异步IO常用于Proactor. 目录 1. 服务器编程框架简介2. IO处理1. R…...

PySpark(三)RDD持久化、共享变量、Spark内核制度,Spark Shuffle
目录 RDD持久化 RDD 的数据是过程数据 RDD 缓存 RDD CheckPoint 共享变量 广播变量 累加器 Spark 内核调度 DAG DAG 的宽窄依赖和阶段划分 内存迭代计算 Spark是怎么做内存计算的? DAG的作用?Stage阶段划分的作用? Spark为什么比MapReduce快? Spar…...
详解MYSQL中的平均值组大小
文章目录 平均值组大小了解平均值组大小MySQL什么时候会使用平均值组大小平均值组大小对于索引选取的影响平均值组大小 了解平均值组大小 总数据量 / 值组 = 平均值组大小 值组是一组具有相同键前缀值的行,及所有相等的键为一个值组。总数据量为全表数据量MySQL什么时候会使…...

【爬虫专区】批量下载PDF (无反爬)
天命:只要没反爬,一切都简单 这次爬取的是绿盟的威胁情报的PDF 先看一下结构,很明显就是一个for循环渲染 burp抓包会发现第二次接口请求 接口请求一次就能获取到了所有的数据 然后一个循环批量下载数据即可,其实没啥难度的 imp…...
PostgreSQL解决序列(自增id)自动增长冲突
背景 一般表的id主键我们都是设置为自增序列。 但是如果我们在插入一些数据的时候手动指定id,那么自增序列不会跟随我们手动设置的id增长。 就会出现下次不设置id的时候自增到我们手动指定的id导致主键冲突bug 举个例子 现在数据有 id123 现在我们手动插入数…...

1.0 Zookeeper 分布式配置服务教程
ZooKeeper 是 Apache 软件基金会的一个软件项目,它为大型分布式计算提供开源的分布式配置服务、同步服务和命名注册。 ZooKeeper 的架构通过冗余服务实现高可用性。 Zookeeper 的设计目标是将那些复杂且容易出错的分布式一致性服务封装起来,构成一个高…...
(Flutter 常用插件整理
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 Flutter 常用插件整理 # Flutter 城市列表,联系人列表,索引&悬停 https://github.com/flutterchina/azlistviewazlistview: ^2.0.0# Dart 汉字转拼音 https://github.com/flutterchina/lpinyinlpinyin…...

vue2.0+使用md-edit编辑器
前言:小刘开发过程中,如果是博客项目一般是会用到富文本。众多富文本中,小刘选择了markdown,并记录分享了下来。 # 使用 npm npm i kangc/v-md-editor -Smain.js基本配置import VueMarkdownEditor from kangc/v-md-editor; import…...

Java设计模式大全:23种常见的设计模式详解(二)
本系列文章简介: 设计模式是在软件开发过程中,经过实践和总结得到的一套解决特定问题的可复用的模板。它是一种在特定情境中经过验证的经验和技巧的集合,可以帮助开发人员设计出高效、可维护、可扩展和可复用的软件系统。设计模式提供了一种在…...

【算法与数据结构】718、1143、1035、392、115、LeetCode最长重复子数组+最长公共子序列+不相交的线+判断子序列+不同的子序列
文章目录 一、718、最长重复子数组二、1143、最长公共子序列三、1035、不相交的线四、392、判断子序列五、115、不同的子序列六、完整代码 所有的LeetCode题解索引,可以看这篇文章——【算法和数据结构】LeetCode题解。 一、718、最长重复子数组 思路分析࿱…...

OCR文本纠错思路
文字错误类别:多字 少字 形近字 当前方案 文本纠错思路 简单: 一、构建自定义词典,提高分词正确率。不在词典中,也不是停用词,分成单字的数据极有可能是错字(少部分可能是新词)。错字与前后的…...
【java批量导出pdf】优化方案
问题情境: 项目中存在web页面点击一键导出,导出所有数据对应的pdf文件,由于有些pdf文件是实时生成的,之前最简答的写法for循环处理速度太慢,超过了nginx配置的最大响应时间了,且对用户交互体验上很不友好&…...

Linux第42步_移植ST公司uboot的第3步_uboot命令测试,搭建nfs服务器和tftp服务器
测试uboot命令,搭建nfs服务器和tftp服务器,是测试uboot非常关键的一步。跳过这一节,后面可能要踩坑。 一、输入“help回车”,查询uboot所支持的命令 二、输入“? bootz回车”,查询“bootz”怎么用 注意:和…...
C++枚举算法(3)
我家的门牌号 题目描述: 我家住在一条短胡同里,这条胡同的门牌号从1开始顺序编号。 若所有的门牌号之和减去我家门牌号的两倍,恰好等于n,求 我家的门牌号及总共有多少家。 数据保证有唯一解。 输入 一个正整数n。n < 100000。…...

【51单片机】LED的三个基本项目(LED点亮&LED闪烁&LED流水灯)(3)
前言 大家好吖,欢迎来到 YY 滴单片机系列 ,热烈欢迎! 本章主要内容面向接触过单片机的老铁 主要内容含: 欢迎订阅 YY滴C专栏!更多干货持续更新!以下是传送门! YY的《C》专栏YY的《C11》专栏YY的…...

Day 17------C语言收尾之链表的删除、位运算、预处理、宏定义
链表 空链表: 注意:函数不能返回局部变量的地址 操作: 1.创建空链表 2.头插 3.尾插 4.链表遍历 5.链表的长度 free:释放 删除: 头删 void popFront(struct Node *head) { //1.p指针变量指向首节点 //2.断…...

【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

【机器视觉】单目测距——运动结构恢复
ps:图是随便找的,为了凑个封面 前言 在前面对光流法进行进一步改进,希望将2D光流推广至3D场景流时,发现2D转3D过程中存在尺度歧义问题,需要补全摄像头拍摄图像中缺失的深度信息,否则解空间不收敛…...

家政维修平台实战20:权限设计
目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系,主要是分成几个表,用户表我们是记录用户的基础信息,包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题,不同的角色…...

Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...
【git】把本地更改提交远程新分支feature_g
创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...
JDK 17 新特性
#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持,不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的ÿ…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
重启Eureka集群中的节点,对已经注册的服务有什么影响
先看答案,如果正确地操作,重启Eureka集群中的节点,对已经注册的服务影响非常小,甚至可以做到无感知。 但如果操作不当,可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...