前言
当今互联网时代,数据处理已成为我们工作中的重要一环。无论是在企业管理中,还是在个人日常生活中,数据的管理和分析都至关重要。在数据处理中,导出数据到Excel或CSV文件成为一种非常普遍的需求。例如我们需要将数据导出到Excel文件供运营或者财务人员分析使用,数据导出到CSV文件提供CRM系统导入。本篇技术博客将介绍使用Hutool工具生成Excel和CSV文件的基本流程,并演示如何在一个接口中通过将Excel和CSV文件压缩到Zip压缩包中且不生成本地文件。
Hutool工具简介
Hutool是一个小而全的Java工具类库。它包含了一系列的工具类,如字符串分割、替换、格式化等的字符串处理工具、文件读取、写入、复制、移动等文件操作工具、日期格式化、计算、转换等功能的日期时间处理工具等等可以帮助Java开发者提高开发效率,满足大多数的Java开发中的工具类的需求,减少重复劳动。Hutool官网地址
Hutool提供了如下工具组件:
模块 | 介绍 |
---|---|
hutool-aop | JDK动态代理封装,提供非IOC下的切面支持 |
hutool-bloomFilter | 布隆过滤,提供一些Hash算法的布隆过滤 |
hutool-cache | 简单缓存实现 |
hutool-core | 核心,包括Bean操作、日期、各种Util等 |
hutool-cron | 定时任务模块,提供类Crontab表达式的定时任务 |
hutool-crypto | 加密解密模块,提供对称、非对称和摘要算法封装 |
hutool-db | JDBC封装后的数据操作,基于ActiveRecord思想 |
hutool-dfa | 基于DFA模型的多关键字查找 |
hutool-extra | 扩展模块,对第三方封装(模板引擎、邮件、Servlet、二维码、Emoji、FTP、分词等) |
hutool-http | 基于HttpUrlConnection的Http客户端封装 |
hutool-log | 自动识别日志实现的日志门面 |
hutool-script | 脚本执行封装,例如Javascript |
hutool-setting | 功能更强大的Setting配置文件和Properties封装 |
hutool-system | 系统参数调用封装(JVM信息等) |
hutool-json | JSON实现 |
hutool-captcha | 图片验证码实现 |
hutool-poi | 针对POI中Excel和Word的封装 |
hutool-socket | 基于Java的NIO和AIO的Socket封装 |
hutool-jwt | JSON Web Token (JWT)封装实现 |
使用方式如下:
🍊Maven
在项目的pom.xml的dependencies中加入以下内容:
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.16</version>
</dependency>
🍐Gradle
implementation 'cn.hutool:hutool-all:5.8.16'
因为小编的工作岗位每天都要都要写日报,疫情期间由直接文字提交变为了使用Excel制式文件(加剧内卷???),在写了几个月以后终于借助某个需要导出Excel文件的需求顺势来生成Excel工作日报,本文借助我的工作日报演示生成Excel文件,主要包含了Excel单元格字体设置、合并单元格合、自动换行、设置背景颜色等主要Excel操作。整体的设想为通过代码生成Excel日志制式文件,在输入给ChatGPT本周的工作目标借助ChatGPT的废话文学特点自动填充日报,达到自动写日报的目的。目前实现了生成日报制式文件功能,先记录下来。工作日报Excel制式主要包含今日复盘和明日工作计划文件如下:
- 今日复盘如下:
- 明天规划如下:
Excel的字体和样式
分析上面的工作日报文件每天都包含一个今日工作复盘和明日工作规划,同时TODO List 字体大小与其它内容的字体不一样,且有不同的背景颜色, 字体加粗,内容自动换行。
-
添加Hutool依赖
<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.15</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.2.2</version> </dependency>
Hutool-all中包含了Hutool的所有工具类,由于需要生成Excel文件需要依赖poi(ps:许多人诟病Hutool的原因之一?)
-
Excel文件Cell字体设置
主要设置Excel字体名称、字体大小、字体颜色、下划线、是否加粗、是否斜体、是否有删除线等常用的字体设置方法,代码如下:
/** * 单元格字体常用设置基础方法 * * @param writer ExcelWriter * @param fontName 字体名称(ex:微软雅黑、宋体、仿宋,需要底层操作系统支持) * @param fontSize 字体大小 * @param color 字体颜色 * @param underline 下划线样式 * @param bold 是否加粗 * @param strikeout 是否有删除线 * @param italic 是否斜体 * @return Font */ public static Font createCellBaseFont(ExcelWriter writer, String fontName, short fontSize, short color, byte underline, boolean bold, boolean strikeout, boolean italic) { // 创建单元格Cell字体 Font font = writer.createFont(); // 设置字体名称(ex:微软雅黑、宋体、仿宋) font.setFontName(fontName); // 设置字体大小 font.setFontHeightInPoints(fontSize); // 设置字体颜色 font.setColor(color); // 设置字体是否加粗 font.setBold(bold); // 设置字体下划线样式 font.setUnderline(underline); // 设置是否有删除线 font.setStrikeout(strikeout); // 设置是否斜体 font.setItalic(italic); return font; }
-
Excel单元格样式设置
Excel单元格样式设置主要包括单元格水平方向对齐方式、垂直方向对齐方式、单元格内容字体样式、单元格背景颜色、单元格边框等常用设置,代码如下:
/** * 设置单元格的基础常用样式 * * @param writer Excel * @param font 字体 * @param fillForegroundColor 是否填充背景 * @param color 背景颜色 fillForegroundColor为true时生效 * @param hAlign 水平对齐样式 * @param vAlign 垂直对齐样式 * @param wrapText 是否自动换行 * @return CellStyle */ public static CellStyle createCellBaseStyle(ExcelWriter writer, Font font, boolean fillForegroundColor, short color, HorizontalAlignment hAlign, VerticalAlignment vAlign, boolean wrapText) { // 创建单元格样式 CellStyle style = writer.getWorkbook().createCellStyle(); // 设置单元格内容的水平对齐方式,如左对齐、居中对齐、右对齐等。 style.setAlignment(hAlign); // 设置单元格内容的垂直对齐方式,如上对齐、居中对齐、下对齐等。 style.setVerticalAlignment(vAlign); // 设置字体 style.setFont(font); // 是否设置背景色 if (fillForegroundColor) { // style.setFillForegroundColor(IndexedColors.PALE_BLUE.getIndex()); // 设置单元格背景颜色 style.setFillForegroundColor(color); // 设置填充模式 style.setFillPattern(FillPatternType.SOLID_FOREGROUND); } // 设置边框 style.setBorderTop(BorderStyle.THIN); style.setBorderBottom(BorderStyle.THIN); style.setBorderLeft(BorderStyle.THIN); style.setBorderRight(BorderStyle.THIN); style.setWrapText(wrapText); return style; }
-
合并单元格操作
样例文件中包含了很多单元格合并操作,其中第一行合并,第一列合并,第二行从第二列合并到第七列并且有填充内容 To Do List 复盘总结 — 03月20日(星期一),第三列需要从第6行到第十三行竖着合并单元格并且填充项目名称等等。在Hutool中提供了merge方法来进行单元格的合并操作,Hutool中的ExcelWiter中的merge方法如下所示:
/** * 合并当前行的单元格<br> * 样式为默认标题样式,可使用{@link #getHeadCellStyle()}方法调用后自定义默认样式 * * @param lastColumn 合并到的最后一个列号 * @return this */ public ExcelWriter merge(int lastColumn) { return merge(lastColumn, null); } /** * 合并当前行的单元格,并写入对象到单元格<br> * 如果写到单元格中的内容非null,行号自动+1,否则当前行号不变<br> * 样式为默认标题样式,可使用{@link #getHeadCellStyle()}方法调用后自定义默认样式 * * @param lastColumn 合并到的最后一个列号 * @param content 合并单元格后的内容 * @return this */ public ExcelWriter merge(int lastColumn, Object content) { return merge(lastColumn, content, true); } /** * 合并某行的单元格,并写入对象到单元格<br> * 如果写到单元格中的内容非null,行号自动+1,否则当前行号不变<br> * 样式为默认标题样式,可使用{@link #getHeadCellStyle()}方法调用后自定义默认样式 * * @param lastColumn 合并到的最后一个列号 * @param content 合并单元格后的内容 * @param isSetHeaderStyle 是否为合并后的单元格设置默认标题样式,只提取边框样式 * @return this * @since 4.0.10 */ public ExcelWriter merge(int lastColumn, Object content, boolean isSetHeaderStyle) { Assert.isFalse(this.isClosed, "ExcelWriter has been closed!"); final int rowIndex = this.currentRow.get(); merge(rowIndex, rowIndex, 0, lastColumn, content, isSetHeaderStyle); // 设置内容后跳到下一行 if (null != content) { this.currentRow.incrementAndGet(); } return this; } /** * 合并某行的单元格,并写入对象到单元格<br> * 如果写到单元格中的内容非null,行号自动+1,否则当前行号不变<br> * 样式为默认标题样式,可使用{@link #getHeadCellStyle()}方法调用后自定义默认样式 * * @param firstRow 起始行,0开始 * @param lastRow 结束行,0开始 * @param firstColumn 起始列,0开始 * @param lastColumn 结束列,0开始 * @param content 合并单元格后的内容 * @param isSetHeaderStyle 是否为合并后的单元格设置默认标题样式,只提取边框样式 * @return this * @since 4.0.10 */ public ExcelWriter merge(int firstRow, int lastRow, int firstColumn, int lastColumn, Object content, boolean isSetHeaderStyle) { Assert.isFalse(this.isClosed, "ExcelWriter has been closed!"); CellStyle style = null; if (null != this.styleSet) { style = styleSet.getStyleByValueType(content, isSetHeaderStyle); } return merge(firstRow, lastRow, firstColumn, lastColumn, content, style); } /** * 合并单元格,并写入对象到单元格,使用指定的样式<br> * 指定样式传入null,则不使用任何样式 * * @param firstRow 起始行,0开始 * @param lastRow 结束行,0开始 * @param firstColumn 起始列,0开始 * @param lastColumn 结束列,0开始 * @param content 合并单元格后的内容 * @param cellStyle 合并后单元格使用的样式,可以为null * @return this * @since 5.6.5 */ public ExcelWriter merge(int firstRow, int lastRow, int firstColumn, int lastColumn, Object content, CellStyle cellStyle) { Assert.isFalse(this.isClosed, "ExcelWriter has been closed!"); CellUtil.mergingCells(this.getSheet(), firstRow, lastRow, firstColumn, lastColumn, cellStyle); // 设置内容 if (null != content) { final Cell cell = getOrCreateCell(firstColumn, firstRow); CellUtil.setCellValue(cell, content, cellStyle); } return this; }
日报中合并单元格操作代码如下:
// 创建一个ExcelWriter 用来写Excel文件,如果为xlsx ExcelWriter writer = new ExcelWriter(true); // 第一个Sheet名称 writer.renameSheet(0, "test"); // 合并第0列 writer.merge(0); // 合并第0列(0到30行,不填充内容) writer.merge(0, 30, 0, 0, "", null);
-
跳过N行
// 跳过两行 writer.passRows(2);
-
获取某个单元格填充内容并设置样式
由于日志中包含了一些固定字段设置,如果每日复盘中的序号、项目归属、任务归属、完成情况、协调资源等,所以写了一个方法,通过获取固定的单元格填充内容并设置单元格样式
/** * 今日复盘中的固定表头 */ private static final String[] REPLAY_TITLE = { "序号", "项目归属", "任务", "完成情况", "资源协助", "原因分析(未完成工作需写原因)" }; /** * 设置复盘总结表头并设置样式 * * @param writer ExcelWriter * @param fields 表头固定字段名称 * @param y 列 */ public static void createReplayTitleAndStyle(ExcelWriter writer, String[] fields, int y) { for (int i = 0; i < fields.length; i++) { // 获取单元格 第一个参数为列号,第二个参数为行号 Cell cell = writer.getCell(i + 1, y, true); // 设置单元格样式 cell.setCellStyle(ExcelCellStyleUtil.createSimpleCellStyle(writer, (short) 12)); // 填充单元格内容 cell.setCellValue(fields[i]); } }
-
设置行高
writer.setRowHeight(0, 10); writer.setRowHeight(1, 30);
-
设置列宽
writer.setColumnWidth(0, 2); writer.setColumnWidth(1, 15);
-
设置自动列宽
// 设置自动换行(如果指定了想要保持设定好的列宽和行高,则需要将此设置提前) writer.autoSizeColumnAll(); // 设置列宽行高 setJobLogRowHeight(writer); setJobLogColumnWidth(writer);
-
设置特殊单元格
StyleSet styleSet = writer.getStyleSet(); CellStyle cellStyle = styleSet.getCellStyle(); // 数字保留小数 例:保留两位小数 CellStyle cellStyleForNumber = styleSet.getCellStyleForNumber(); cellStyleForNumber.setDataFormat((short) 2); // 时间格式化 例如格式为:YYYY/MM/dd 格式 CellStyle cellStyleForDate = styleSet.getCellStyleForDate(); // 代表的时间格式是 yyyy/MM/dd cellStyleForDate.setDataFormat((short) 14);
生成效果
在对生成Excel日志文件进行分析后分为将每天的日志分为三个大的部分:工作信息、今日复盘和明日计划,其中工作信息中包含了例如:部门、岗位、姓名等属性;今日复盘包含了本周目标信息列表、今日工作列表以及个人总结成长部分;明日计划中包含了明日工作任务。将这些整理为Java的对象,方便使用,由于篇幅有限,将代码放置在了git仓库中。
写一个单元测试,检查一下效果
@Test
void testGenerateJobLogSheet(){
// 工作日志
JobLog jobLog = new JobLog();
// 工作信息
JobInfo jobInfo = new JobInfo();
jobInfo.setDepartment("创新部");
jobInfo.setPost("Java开发工程师");
jobInfo.setName("小赵小赵");
jobInfo.setProject("XX(8)");
jobLog.setJobInfo(jobInfo);
// 今日复盘
Replay replay = new Replay();
List<Goal> goals = new ArrayList<>();
for (int i = 1; i < 4; i++) {
Goal goal = new Goal("今日目标" + i);
goals.add(goal);
}
replay.setGoals(goals);
List<Task> tasks = new ArrayList<>();
for (int i = 1; i < 9; i++) {
Task task = new Task();
task.setTask("今日任务" + i);
task.setCompletion(100);
tasks.add(task);
}
replay.setTasks(tasks);
Extend extend = new Extend();
replay.setExtend(extend);
jobLog.setReplay(replay);
Plan plan = new Plan(tasks);
jobLog.setPlan(plan);
ExcelWriter writer = GenerateJobLog.generateSheet(LocalDate.now(), jobLog);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String today = LocalDate.now().format(formatter);
String excelName = "日报-TDL-" + jobLog.getJobInfo().getDepartment() + "-" + jobLog.getJobInfo().getName() + "-" + today + ".xlsx";
File excelFile = new File(excelName);
writer.flush(excelFile);
writer.close();
}
通过代码生成的日志Excel文件效果如下:
不足与改善
存在的不足还有很多,例如没有判断节假日和调休以及每日自动生成日志并提交;后期愿景就是借助ChatGPT的废话文学特色进行给定每周的目标,自动填充每日的任务复盘和明日的工作复盘或者是对目标、每日任务等进行润色,同时还可以将这些工作日报信息进行存储,通过这些日报生成周报、月报以及年度总结等。