easyExcel 3.x以上版本导入数据后,再把错误信息导出,外加自定义RGB背景色、行高、宽度等
easyExcel 3.x以上版本导入数据后,再把错误信息导出,外加自定义RGB背景色
背景
由于项目中用的easypoi导入的数据量大了,会导致OOM的问题,所以要求更换为easyExcel框架做导入。话不多说,这里只做一个导入的示例,还有把遇到的一些问题列出来,大家根据业务需要随机应变。文章参考了其他大佬写的博客,这里把参考的大佬博客列出来:
官方文档:https://easyexcel.opensource.alibaba.com/docs/3.0.x
https://blog.csdn.net/qq_36978700/article/details/123425954
https://blog.csdn.net/qq_29834241/article/details/133786536
https://blog.csdn.net/wjs_007/article/details/135118539
https://www.cnblogs.com/mike-mei/p/17732227.html
引入依赖
//我的项目用的是gradle
implementation ('com.alibaba:easyexcel:3.0.5')
//maven
<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.0.5</version>
</dependency>
//可能会用到alibaba的fastjson
implementation 'com.alibaba:fastjson:1.2.83'
Controller代码
@PostMapping("/import")public JSONResult importExcel(@RequestParam(name = "file") MultipartFile file, HttpServletResponse response) {try {//实现easyExcel的解析对象DemoDataListener demoDataListener = new DemoDataListener();//读取excel文件EasyExcel.read(file.getInputStream(), DemoData.class, demoDataListener).sheet().doRead();List<Map<String, Object>> failDataList = demoDataListener.getFailDataList();//导出错误数据export(dataList(failDataList), response);} catch (Exception e) {log.error("导入配置异常", e);}return JSONResult.ok("成功");}private void export(List<DemoData> dataList,HttpServletResponse response) {// 头的策略WriteCellStyle headWriteCellStyle = new WriteCellStyle();// 背景设置为红色headWriteCellStyle.setFillForegroundColor(IndexedColors.DARK_RED.getIndex());WriteFont headWriteFont = new WriteFont();headWriteFont.setFontName("宋体");headWriteFont.setFontHeightInPoints((short)11);headWriteFont.setBold(true);headWriteFont.setColor(IndexedColors.WHITE.getIndex());headWriteCellStyle.setBorderRight(BorderStyle.THIN);headWriteCellStyle.setRightBorderColor(IndexedColors.WHITE.getIndex());headWriteCellStyle.setWriteFont(headWriteFont);// 内容的策略WriteCellStyle contentWriteCellStyle = new WriteCellStyle();// 设置细边框contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);contentWriteCellStyle.setBorderRight(BorderStyle.THIN);contentWriteCellStyle.setBorderTop(BorderStyle.THIN);// 设置边框颜色 25灰度contentWriteCellStyle.setBottomBorderColor(IndexedColors.GREEN.getIndex());contentWriteCellStyle.setTopBorderColor(IndexedColors.GREEN.getIndex());contentWriteCellStyle.setLeftBorderColor(IndexedColors.GREEN.getIndex());contentWriteCellStyle.setRightBorderColor(IndexedColors.GREEN.getIndex());WriteFont contentWriteFont = new WriteFont();contentWriteFont.setFontName("宋体");// 字体大小contentWriteFont.setFontHeightInPoints((short)12);contentWriteCellStyle.setWriteFont(contentWriteFont);// 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);try (ServletOutputStream outputStream = response.getOutputStream()) {String fileName = "demo_error_data" + System.currentTimeMillis();response.setContentType("application/vnd.ms-excel");response.setCharacterEncoding("utf8");response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".xlsx");response.setHeader("Pragma", "public");response.setHeader("Cache-Control", "no-store");response.addHeader("Cache-Control", "max-age=0");EasyExcel.write(outputStream, DemoData.class).registerWriteHandler(horizontalCellStyleStrategy).registerWriteHandler(new CustomRgbCellStyle(contentWriteCellStyle))//自定义行高,宽度.registerWriteHandler(new SimpleRowHeightStyleStrategy((short) 21,(short) 19))//设置自动列宽.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).head(head()).sheet("Sheet").doWrite(dataList);} catch (IOException e) {throw new RuntimeException("导出excel表格失败!", e);}}/*** 自定义表头,如不需要自定义表头,直接在DemoData对象的注解@ExcelProperty配置即可*/private List<List<String>> head() {List<List<String>> list = new ArrayList<>();List<String> head0 = new ArrayList<>();head0.add("标题");List<String> head1 = new ArrayList<>();head1.add("创建时间");List<String> head2 = new ArrayList<>();head2.add("价格");List<String> head3 = new ArrayList<>();head3.add("名称");List<String> head4 = new ArrayList<>();head4.add("规格");List<String> head5 = new ArrayList<>();head5.add("失败原因");list.add(head0);list.add(head1);list.add(head2);list.add(head3);list.add(head4);list.add(head5);return list;}private List<DemoData> dataList(List<Map<String, Object>> failList) {List<DemoData> list = ListUtils.newArrayList();for (Map<String, Object> map : failList) {list.add((DemoData) map.get("data"));}log.info("Data ===========> {}", JSON.toJSONString(list));return list;}
实体类DemoData
@Data
public class DemoData {@ExcelProperty(index = 0)//index可不写,表示读取的列下标private String title;@ExcelProperty(index = 1)private String createTime;@ExcelProperty(index = 2)private BigDecimal price;@ExcelProperty(index = 3)private String pname;@ExcelProperty(index = 4)private String spec;@ExcelProperty(index = 5)private String failReason;
}
解析对象DemoDataListener
@Slf4j
public class DemoDataListener implements ReadListener<DemoData> {/*** 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收*/private static final int BATCH_COUNT = 100;/*** 错误数据上限,达到上限则停止解析数据*/private static final int ERROR_COUNT = 100;/*** 缓存的数据*/private List<DemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);/*** 失败的数据*/private List<Map<String, Object>> failDataList = new ArrayList<>();private DemoDao dao;public DemoDataListener() {// TODO 实际使用过程中可通过构造参数把dao层的对象传进来,写入数据(下面注释的构造参数)}/**public DemoDataListener(DemoDao dao) {// TODO 实际使用过程中可通过构造参数把dao层的对象传进来,写入数据this.dao = dao;}*/public List<Map<String, Object>> getFailDataList() {return failDataList;}@Overridepublic void onException(Exception exception, AnalysisContext context) throws Exception {log.error("解析数据异常!", exception);
// if (failDataList.size() > 0) {
// exportErrorDataToExcel();
// }ReadListener.super.onException(exception, context);}@Overridepublic void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {ReadListener.super.invokeHead(headMap, context);}/*** 这个每一条数据解析都会来调用** @param data one row value. Is is same as {@link AnalysisContext#readRowHolder()}* @param context*/@Overridepublic void invoke(DemoData data, AnalysisContext context) {//去除空行,有些空行有格式但没有数据,也会被读取if (lineNull(data)) {return;}log.info("解析到一条数据:{}", JSON.toJSONString(data));//TODO 这里做数据校验,失败的数据放到failDataList中if (StringUtils.isBlank(data.getPname())) {Map<String, Object> map = new HashMap<>(1);data.setFailReason("第"+context.readRowHolder().getRowIndex()+"行:商品名称不能为空");map.put("data", data);failDataList.add(map);}
// if (failDataList.size() > ERROR_COUNT) {
// throw new RuntimeException("错误数据过多,停止解析,请检查数据");
// }//校验数据完成后,添加到缓存中cachedDataList.add(data);// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOMif (cachedDataList.size() >= BATCH_COUNT) {saveData();// 存储完成清理 listcachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);}}private boolean lineNull(Object line) {if (line instanceof String) {return StringUtils.isEmpty((String) line);}try {Set<Field> fields = Arrays.stream(line.getClass().getDeclaredFields()).filter(f -> f.isAnnotationPresent(ExcelProperty.class)).collect(Collectors.toSet());for (Field field : fields) {field.setAccessible(true);if (field.get(line) != null) {return false;}}return true;} catch (Exception ignored) {log.error(ignored.getMessage(), ignored);}return true;}@Overridepublic void extra(CellExtra extra, AnalysisContext context) {ReadListener.super.extra(extra, context);}@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {}@Overridepublic boolean hasNext(AnalysisContext context) {return ReadListener.super.hasNext(context);}/*** 加上存储数据库*/private void saveData() {log.info("{}条数据,开始存储数据库!", cachedDataList.size());//TODO 这里执行存储数据库的代码逻辑
// demoDAO.save(cachedDataList);log.info("存储数据库成功!");}
}
自定义RGB样式CustomRgbCellStyle
@Slf4j
public class CustomRgbCellStyle extends AbstractCellStyleStrategy {private List<WriteCellStyle> contentWriteCellStyleList;public CustomRgbCellStyle(WriteCellStyle contentWriteCellStyle) {if (contentWriteCellStyle != null) {this.contentWriteCellStyleList = ListUtils.newArrayList(contentWriteCellStyle);}}@Overridepublic void setHeadCellStyle(CellWriteHandlerContext context) {Boolean head = context.getHead();// 设置标题头样式,这里的判断可不要if (head) {log.info("afterCellCreate ====> {}", head);// 获取和创建CellStyleWriteCellData<?> cellData = context.getFirstCellData();CellStyle originCellStyle = cellData.getOriginCellStyle();if (Objects.isNull(originCellStyle)) {originCellStyle = context.getWriteWorkbookHolder().getWorkbook().createCellStyle();}// 设置背景颜色((XSSFCellStyle) originCellStyle).setFillForegroundColor(new XSSFColor(new java.awt.Color(186, 12, 47), new DefaultIndexedColorMap()));originCellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);// 重点!!! 由于在FillStyleCellWriteHandler,会把OriginCellStyle和WriteCellStyle合并,会已WriteCellStyle样式为主,所有必须把WriteCellStyle设置的背景色清空// 具体合并规则请看WriteWorkbookHolder.createCellStyle方法WriteCellStyle writeCellStyle = cellData.getWriteCellStyle();writeCellStyle.setFillForegroundColor(null);// 重点!!! 必须设置OriginCellStylecellData.setOriginCellStyle(originCellStyle);}}@Overridepublic void setContentCellStyle(CellWriteHandlerContext context) {if (stopProcessing(context) || CollectionUtils.isEmpty(contentWriteCellStyleList)) {return;}WriteCellData<?> cellData = context.getFirstCellData();if (context.getRelativeRowIndex() == null || context.getRelativeRowIndex() <= 0) {WriteCellStyle.merge(contentWriteCellStyleList.get(0), cellData.getOrCreateStyle());} else {WriteCellStyle.merge(contentWriteCellStyleList.get(context.getRelativeRowIndex() % contentWriteCellStyleList.size()),cellData.getOrCreateStyle());}}protected boolean stopProcessing(CellWriteHandlerContext context) {return context.getFirstCellData() == null;}
}
到此结束,如有疑问,欢迎留言!
相关文章:
easyExcel 3.x以上版本导入数据后,再把错误信息导出,外加自定义RGB背景色、行高、宽度等
easyExcel 3.x以上版本导入数据后,再把错误信息导出,外加自定义RGB背景色 背景 由于项目中用的easypoi导入的数据量大了,会导致OOM的问题,所以要求更换为easyExcel框架做导入。话不多说,这里只做一个导入的示例&…...
React的img图片路径怎么写
在React中,图片路径的写法取决于你的图片资源是如何被管理和存放的。这里有几种常见的情况和对应的写法: 1. 图片作为React组件的静态资源 如果你的图片文件放在React项目的public文件夹下(这是Create React App项目的默认结构)…...

UGUI优化篇--UGUI合批
UGUI合批 UGUI合批规则概述UGUI性能查看工具合批部分的特殊例子一个白色image、蓝色image覆盖了Text,白色image和Text哪个先渲染 Mask合批Mask为什么会产生两个drawcallMask为什么不能合批Mask注意要点 RectMask2D为什么RecMask2D比Mask性能更好主要代码RectMask2D注…...

FineBI连接MySQL5.7
一、在FineBI系统管理中,点击【新建数据库连接】 选择MySQL数据库 配置数据库连接,如下,其中数据库名称就是需要连接的目标数据库...

基于tkinter的学生信息管理系统之登录界面和主界面菜单设计
目录 一、tkinter的介绍 二、登陆界面的设计 1、登陆界面完整代码 2、部分代码讲解 3、登录的数据模型设计 4、效果展示 三、学生主界面菜单设计 1、学生主界面菜单设计完整代码 2、 部分代码讲解 3、效果展示 四、数据库的模型设计 欢迎大家进来学习和支持!…...

web基础以及http协议
⼀、web基本概念和常识 Web:为⽤户提供的⼀种在互联⽹上浏览信息的服务,Web 服 务是动态的、可交 互的、跨平台的和图形化的。 Web 服务为⽤户提供各种互联⽹服务,这些服务包括信息浏览 服务,以及各种交互式服务,包括…...

DataEase一键部署:轻松搭建数据可视化平台
DataEase是一个开源的数据可视化和分析工具,旨在帮助用户轻松创建和共享数据仪表盘。它支持多种数据源,包括关系型数据库,文件数据源,NoSQL数据库等,提供强大的数据查询、处理和可视化功能。DataEase 不仅是一款数据可…...
网络安全相关竞赛比赛
赛事日历(包含全国所有网络安全竞赛) https://datacon.qianxin.com/competition/competitions https://www.ichunqiu.com/competition/all 全国网络安全竞赛 名称链接全国大学生信息安全竞赛http://www.ciscn.cn/信息安全与对抗技术竞赛(In…...

Vscode——如何快速搜索项目工程中的某个文件的位置
第一步:按 shift ctrl p 第二步:然后把 > 删除 第三步:输入文件名称即可...

Kubernetes 正在弃用 Docker?Docker将何去何从?
一段时间以来,当人们想到容器时,似乎都会想到Docker和Kubernetes。在构建和运行容器方面,Docker 一直是大名鼎鼎的品牌,而在管理和编排容器方面,Kubernetes 一直是大名鼎鼎的品牌。听到 Kubernetes 从 1.20 版开始不再…...
编程语言「描述符」漫谈——以C++与Rust为例的行为声明与类型描述
编程语言中有三种描述符: 声明符: 表示一种动作, 比如创建变量, 定义函数等等;说明符: 也就是类型说明符, 表示一种数据类型;修饰符: 表示动作或类型的属性, 例如不可变…… swift语言就是严格遵循这些描述符的, 例如, objc是修饰符 , 表示编译成OC兼容函数, func 是声明符, …...

电脑屏幕录制软件哪个好?推荐3款,满足各种录制需求
大家好,今天和大家来聊一个既实用又有点神秘的话题——电脑屏幕录制软件哪个好?这是个让众多网友头疼的问题,毕竟谁不想拥有一款既好用又好玩的录制神器呢? 首先,我们得明确屏幕录制软件可不是简单地录屏而已…...
大模型学习应用 1:用 itrex 创新高效实现 LLM 的部署和微调
用 itrex 创新高效实现 LLM 的部署和微调 - 项目作业 目录 准备工作Task 1 完成在线环境的工具包安装,包含 基础环境包、Extension for Transformers 包、加速计算包Task 2 利用 Intel Extension for Transformers 部署通义千问 Qwen-7B Chat,并根据 pr…...

【Android】碎片—动态添加、创建Fragment生命周期、通信
简单用法 在一个活动中添加两个碎片,并让这两个碎片平分活动空间 先新建一个左侧碎片布局和一个右侧碎片布局 左侧碎片 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/…...

前端 SSE 长连接
使用 const options {withCredentials: true, // 默认 false}const eventSource new EventSource(/api, options);eventSource.addEventListener(open, () > {});eventSource.onmessage (event) > {}; // 或addEventListener(message,callback)eventSource.addEvent…...
.mp4格式的视频为何不能通过video标签在chrome浏览器中播放?
chrome浏览器目前只支持编解码格式为H264格式的视频,如果某个.mp4后缀的视频不能在chrome浏览器中播放,多半是这个视频的编码格式不是H264的! 1、可以通过ffmpeg工具查看当前视频的编码格式: ffprobe -v error -select_streams v…...

Python酷库之旅-第三方库Pandas(051)
目录 一、用法精讲 186、pandas.Series.is_monotonic_increasing属性 186-1、语法 186-2、参数 186-3、功能 186-4、返回值 186-5、说明 186-6、用法 186-6-1、数据准备 186-6-2、代码示例 186-6-3、结果输出 187、pandas.Series.is_monotonic_decreasing属性 187…...
linux timestamp
驱动或应用中获取时间戳的接口。 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <sys/time.h> #if 0 #include <linux/ktime.h> /* 内核驱动中获取时间戳 */ static ktime_t get_kernel_time…...
Vue.js 搭建大屏可视化项目
引言 在数字化转型的时代背景下,大屏可视化项目因其直观的数据展示和实时的业务监控能力而变得日益重要。Vue.js,以其简洁的语法、高效的虚拟DOM和强大的组件化能力,成为了构建大屏可视化应用的首选框架之一。本文将从零开始,引导…...

Linux:进程信号(二.信号的保存与处理、递达、volatile关键字、SIGCHLD信号)
上次介绍了:(Linux:进程信号(一.认识信号、信号的产生及深层理解、Term与Core))[https://blog.csdn.net/qq_74415153/article/details/140624810] 文章目录 1.信号保存1.1递达、未决、阻塞等概念1.2再次理解信号产生与保存1.3信号…...

接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...
零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?
一、核心优势:专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发,是一款收费低廉但功能全面的Windows NAS工具,主打“无学习成本部署” 。与其他NAS软件相比,其优势在于: 无需硬件改造:将任意W…...
Ubuntu系统下交叉编译openssl
一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机:Ubuntu 20.04.6 LTSHost:ARM32位交叉编译器:arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...

C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
反向工程与模型迁移:打造未来商品详情API的可持续创新体系
在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...

Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...