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信号…...
观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...
树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...
服务器硬防的应用场景都有哪些?
服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...
关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...
#Uniapp篇:chrome调试unapp适配
chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器:Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...
push [特殊字符] present
push 🆚 present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中,push 和 present 是两种不同的视图控制器切换方式,它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...
并发编程 - go版
1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...
前端中slice和splic的区别
1. slice slice 用于从数组中提取一部分元素,返回一个新的数组。 特点: 不修改原数组:slice 不会改变原数组,而是返回一个新的数组。提取数组的部分:slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...
