Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

developframework/excel-io

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

97 Commits

Repository files navigation

excel-io

封装poi对Office Excel的输入输出工具,简化简单的导入和导出Excel数据的操作。(暂不支持合并单元格)

<dependency>
 <groupId>com.github.developframework</groupId>
 <artifactId>excel-io</artifactId>
</dependency>

教程

假设存在实体Student包装数据(构造方法略)

public class Student {
 // 姓名
 private String name;
 // 性别
 private Gender gender;
 // 生日
 private LocalDate birthday;
 // 入学时间
 private LocalDateTime createTime;
 // 语文成绩
 private int chineseScore;
 // 数学成绩
 private int mathScore;
 // 英语成绩
 private int englishScore;
 // 总成绩
 private int totalScore;
 // 是否合格
 private Boolean qualified;
 public enum Gender {
 MALE, FEMALE
 }
}
List<Student> students=List.of(
 new Student("小赵",Student.Gender.MALE,LocalDate.of(2002,1,5),LocalDateTime.now(),97,85,95),
 new Student("小钱",Student.Gender.FEMALE,LocalDate.of(1999,12,25),LocalDateTime.now(),92,89,87),
 new Student("小孙",Student.Gender.MALE,LocalDate.of(2001,6,8),LocalDateTime.now(),50,40,45),
 new Student("小李",Student.Gender.FEMALE,LocalDate.of(2003,8,20),LocalDateTime.now(),80,90,72)
);

ExcelIO

使用ExcelIO得到输入输出处理器

写出数据到Excel

List<Student> students=new LinkedList<>();
// 准备数据略
File file = ExcelIO
 .writer(ExcelType.XLSX)
 .load(students,(workbook,builder)->
 builder.columnDefinitions(
 builder.<String>column("name","学生姓名"),
 builder.<Student.Gender>column("gender","性别"),
 builder.<LocalDate>column("birthday","生日"),
 builder.<LocalDateTime>column("createTime","入学时间"),
 builder.<Integer>column("chineseScore","语文成绩"),
 builder.<Integer>column("mathScore","数学成绩"),
 builder.<Integer>column("englishScore","英语成绩"),
 builder.<Integer>formula("总成绩","SUM(E{row}:G{row})"),
 builder.<Boolean>formula("是否合格","IF(H{row} >= 180,\"合格\",\"不合格\")")
 )
 )
 .writeToFile("D:\\学生成绩表.xlsx");

从Excel读取数据

使用excel-io导入students数据

final List<Student> students = ExcelIO
 .reader("D:\\学生成绩表.xlsx")
 .read(Student.class, (workbook, builder) ->
 builder.columnDefinitions(
 builder.<String>column("name"),
 builder.<Student.Gender>column("gender"),
 builder.<LocalDate>column("birthday"),
 builder.<LocalDateTime>column("createTime"),
 builder.<Integer>column("chineseScore"),
 builder.<Integer>column("mathScore"),
 builder.<Integer>column("englishScore"),
 builder.<Integer>formula(Integer.class, "totalScore"),
 builder.<String>formula(String.class, "qualified")
 .readConvert((student, qualified) -> qualified.equals("合格"))
 )
 );

读取时可以不设置列名header

TableDefinition

该接口是表格的定义类,一个定义类代表了一个数据表

通过该接口可以设置表格的表头信息和表格的左上角单元格位置(工作表、行、列)。

new TableDefinition<>() {
 /**
 * 设置表格信息
 */
 @Override
 public TableInfo tableInfo() {
 return new TableInfo();
 }
 /**
 * 列定义
 */
 @Override
 public ColumnDefinition<Student>[] columnDefinitions(Workbook workbook, ColumnDefinitionBuilder builder) {
 return builder.columnDefinitions(
 
 );
 }
 /**
 * 全局单元格样式处理
 */
 @Override
 public BiConsumer<Workbook, CellStyle> globalCellStylesHandle() {
 return null;
 }
 /**
 * 申明自定义单元格样式
 */
 @Override
 public Map<String, CellStyle> customCellStyles(Workbook workbook) {
 return Collections.emptyMap();
 }
 /**
 * 工作表扩展处理
 */
 @Override
 public SheetExtraHandler<?> sheetExtraHandler() {
 return null;
 }
 /**
 * 装填完的实体单独处理
 */
 @Override
 public void each(Student student) {
 
 }
};

ColumnDefinition

该抽象类是表格的列定义类,一个定义类代表了表中的某一列,指代了一个字段 可通过ColumnDefinitionBuilder方便创建ColumnDefinition的实例

@Override
public ColumnDefinition<Student>[] columnDefinitions(Workbook workbook, ColumnDefinitionBuilder<Student> builder) {
 return builder.columnDefinitions(
 builder.columnDefinitions(
 builder
 .<Student /*实体泛型*/, String /*字段泛型*/>column("name", "学生姓名")
 // 写转化值
 .writeConvert((student, field) -> /* 处理逻辑 */)
 // 读转化值
 .readConvert((student, field) -> /* 处理逻辑 */)
 // 手动设置列宽
 .columnWidth(20)
 // 获得样式键 (可以针对单元格值来选择样式)
 .cellStyleKey((cell, cellValue) -> /* 获得样式键 */)
 )
 );
}
  • builder.column(...)声明一个普通列
  • builder.literal(...)声明一个字面量列
  • builder.blank(...)声明一个空列
  • builder.formula(...)声明一个公式列

如果上述快捷写法无法满足场景要求,需要更加灵活的写入和读取方式。可以直接实现ColumnDefinition接口

@Override
public ColumnDefinition<Student>[] columnDefinitions(Workbook workbook, ColumnDefinitionBuilder builder) {
 return builder.columnDefinitions(
 new ColumnDefinition<>() {
 /**
 * 列信息
 */
 @Override
 public ColumnInfo getColumnInfo() {
 return new ColumnInfo("name", "学生姓名", 10);
 }
 
 /**
 * 描述如何把值写入单元格
 */
 @Override
 public Object writeIntoCell(Workbook workbook, Cell cell, Student student) {
 final String name = student.getName();
 cell.setCellValue(name);
 return name;
 }
 
 /**
 * 描述如何从单元格读取值并装填到实体
 */
 @Override
 public void readOutCell(Workbook workbook, Cell cell, Student student) {
 final String name = cell.getStringCellValue();
 student.setName(name);
 }
 
 /**
 * 配置单元格格式
 */
 @Override
 public void configureCellStyle(Cell cell, CellStyleManager cellStyleManager, Student entity, Object value) {
 cell.setCellStyle(cellStyleManager.getCellStyle(DefaultCellStyles.STYLE_NORMAL));
 }
 }
 );
}

功能

配置单元格样式

单元格样式管理器

在poi中创建CellStyle的个数是有限制的,xls上限4000个,xlsx上限64000个

如果超限了会报如下两个异常:

The maximum number of cell styles was exceeded.You can define up to 4000 styles in a .xls workbook

The maximum number of Cell Styles was exceeded. You can define up to 64000 style in a .xlsx Workbook

所以提供CellStyleManager单元格样式管理器来管理申明的样式,做到复用CellStyle对象

单元格样式键

用于在CellStyleManager内选取CellStyle的键,可以是自定义的任意字符串。也可以是一个特定结构的字符串用于描述CellStyle的特性(格式类似于层叠样式CSS),预设有如下写法:

  • align 对齐
align {vertical: right; horizontal: center}
可缩写成 a {v: right; h: center}
  • border 边框
border {style: thin; color: #ff0000}
可缩写成 b {style: thin; color: RED}
  • dataFormat 格式
dataFormat {format: 0.00%}
dataFormat {format: 'yyyy-mm-dd hh:mm:ss'} 如果格式中需要包含空格,需要加单引号
可缩写成 df {format: 'yyyy-mm-dd hh:mm:ss'}
  • font 字体
font {size: 16; bold: true; italic: true; family: '宋体'; color: #ff0000}
可缩写成 f {size: 16; bold; italic; family: '宋体'; color: #ff0000} boolean字段是true的话可以缩写
  • foreground 前景色
foreground {color: #aa1199; type: SOLID_FOREGROUND}
可缩写成 fg {color: #aa1199; type: SOLID_FOREGROUND}
  • config 其他配置
config {wrapText: true} 允许单元格内换行
可缩写成 c {wrapText}

可在cellStyleKey()方法内直接使用

builder
 .column(...)
 	// 结合文本块功能写起来更美观
 .cellStyleKey((cell, entity, cellValue) -> """
 a {
 h: right
 }
 font {
 color: #ffff00;
 size: 20
 }
 """;
 )
默认单元格样式键

预设了默认的单元格样式:

默认单元格样式键 样式键 说明 备注
DefaultCellStyles.STYLE_TITLE font {size: 16; bold} 加粗 16号字 标题采用
DefaultCellStyles.STYLE_HEADER font {bold} 加粗 列头采用
DefaultCellStyles.STYLE_BODY <空字符串> 黑色细线边框文字居中 表格内容采用
DefaultCellStyles.STYLE_BODY_BOLD font {bold} 加粗
DefaultCellStyles.STYLE_BODY_ITALIC font {italic} 斜体
DefaultCellStyles.STYLE_BODY_PERCENT dataFormat {format: '0.00%'} 0.00% 百分比格式
DefaultCellStyles.STYLE_BODY_DATETIME dataFormat {format: 'yyyy-mm-dd hh:mm:ss'} 日期时间格式
DefaultCellStyles.STYLE_BODY_DATE dataFormat {format: 'yyyy-mm-dd'} 日期格式
DefaultCellStyles.STYLE_BODY_TIME dataFormat {format: 'hh:mm:ss'} 时间格式
DefaultCellStyles.STYLE_BODY_NUMBER align {horizontal: RIGHT} 文字居右
DefaultCellStyles.STYLE_BODY_NUMBER_2_PRECISION align {horizontal: RIGHT} dataFormat {format: '0.00'} 精度2 文字居右
cell.setCellStyle(cellStyleManager.getCellStyle(DefaultCellStyles.STYLE_BODY));
自定义单元格样式

可以用于覆盖默认单元格样式,也可初始化自定义样式

new TableDefinition<>() {
 /**
 * 申明自定义单元格样式
 */
 @Override
 public Map<String, CellStyle> customCellStyles(Workbook workbook) {
 // final CellStyle cellStyle = workbook.createCellStyle();
 final CellStyle cellStyle = DefaultCellStyles.buildByCellStyleKey(DefaultCellStyles.STYLE_BODY);
 // 对单元格样式做自定义配置
 cellStyle.setDataFormat(...);
 // 如果选择默认样式的键将会覆盖
 return Map.of(DefaultCellStyles.STYLE_BODY, cellStyle);
 }
}

单元格可以根据值来判定选择哪个样式键

builder
 .column(...)
 // 获得样式键 (可以针对单元格值来选择样式)
 .cellStyleKey((cell, entity, cellValue) -> {
 return cellValue == null ? DefaultCellStyles.STYLE_BODY : "customKey";
 })

示例:

依靠单元格的值,将不合格的分数标红色背景

ExcelIO
 .writer(ExcelType.XLSX)
 .load(students, new TableDefinition<>() {
 @Override
 public Map<String, CellStyle> customCellStyles(Workbook workbook) {
 // 设置单元格背景色
 final CellStyle redCellStyle = DefaultCellStyles.buildByCellStyleKey("a {h: right} fg {color: RED}");
 return Map.of("redColor", redCellStyle);
 }
 @Override
 public ColumnDefinition<Student>[] columnDefinitions(Workbook workbook, ColumnDefinitionBuilder builder) {
 // 判定分数大于60
 final CellStyleKeyProvider<Student> scoreKeyProvider = (cell, e, v) -> ((Integer) v) >= 60 ? null : "redColor";
 // 判定分数大于180
 final CellStyleKeyProvider<Student> totalKeyProvider = (cell, e, v) -> ((Integer) v) >= 180 ? null : "redColor";
 // 判定是否合格
 final CellStyleKeyProvider<Student> qualifiedKeyProvider = (cell, e, v) -> v.equals("合格")? null : "redColor";
 return builder.columnDefinitions(
 builder.column("name", "学生姓名"),
 builder.column("gender", "性别"),
 builder.column("birthday", "生日"),
 builder.column("createTime", "入学时间"),
 builder.column("chineseScore", "语文成绩").cellStyleKey(scoreKeyFunction),
 builder.column("mathScore", "数学成绩").cellStyleKey(scoreKeyFunction),
 builder.column("englishScore", "英语成绩").cellStyleKey(scoreKeyFunction),
 builder.formula(Integer.class, "总成绩", "SUM(E{row}:G{row})").cellStyleKey(totalKeyFunction),
 builder.formula(String.class, "是否合格", "IF(H{row} >= 180,\"合格\",\"不合格\")").cellStyleKey(qualifiedKeyFunction)
 );
 }
 }
)
.writeToFile("D:\\学生成绩表.xlsx");

全局单元格样式

对所有注册进单元格样式管理器的CellStyle执行统一处理

new TableDefinition<>(){
 
 /**
 * 全局单元格样式处理
 */
 @Override
 public BiConsumer<Workbook, CellStyle> globalCellStylesHandle() {
 return (workbook, cellStyle) -> {
 // 改变全部字体
 final Font font = workbook.createFont();
 font.setFontName("宋体");
 cellStyle.setFont(font);
 };
 }
}

Demo

制作员工考勤表

@Getter
@Setter
public class StaffAttendance {
 // 员工姓名
 private String name;
 // 考勤记录
 private Attendance[] attendances;
 @Getter
 @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
 public enum Attendance {
 // 出勤
 ON_DUTY("Y"),
		// 请假
 LEAVE("N");
 private final String text;
 }
}
public static List<StaffAttendance> buildStaffAttendances() {
 return List.of(
 createStaffAttendance("张三", new int[]{15, 20}),
 createStaffAttendance("李四", new int[]{3}),
 createStaffAttendance("王五", new int[]{3, 4, 27})
 );
 }
private static StaffAttendance createStaffAttendance(String name, int[] leaveDays) {
 StaffAttendance staffAttendance = new StaffAttendance();
 staffAttendance.setName(name);
 final StaffAttendance.Attendance[] attendances = new StaffAttendance.Attendance[31];
 staffAttendance.setAttendances(attendances);
 Arrays.fill(attendances, StaffAttendance.Attendance.ON_DUTY);
 for (int leaveDay : leaveDays) {
 attendances[leaveDay - 1] = StaffAttendance.Attendance.LEAVE;
 }
 return staffAttendance;
}
final List<StaffAttendance> projects = buildStaffAttendances();
 ExcelIO.writer(ExcelType.XLSX)
 .load(projects, new TableDefinition<>() {
 @Override
 public TableInfo tableInfo() {
 TableInfo tableInfo = new TableInfo();
 tableInfo.hasTitle = true;
 tableInfo.title = "员工考勤表";
 tableInfo.sheetName = "考勤表";
 return tableInfo;
 }
 @Override
 public Map<String, CellStyle> customCellStyles(Workbook workbook) {
 // 自定义标题样式 覆盖原有样式
 final CellStyle titleCellStyle = DefaultCellStyles.buildByCellStyleKey(workbook, "font {size: 16; bold; color: BLUE}");
 return Map.of(DefaultCellStyles.STYLE_TITLE, titleCellStyle);
 }
 @Override
 public BiConsumer<Workbook, CellStyle> globalCellStylesHandle() {
 return (workbook, cellStyle) -> {
 // 修改全局字体
 final Font font = workbook.getFontAt(cellStyle.getFontIndexAsInt());
 font.setFontName("黑体");
 };
 }
 @Override
 @SuppressWarnings("unchecked")
 public ColumnDefinition<StaffAttendance>[] columnDefinitions(Workbook workbook, ColumnDefinitionBuilder<StaffAttendance> builder) {
 List<ColumnDefinition<StaffAttendance>> columnDefinitions = new LinkedList<>();
 columnDefinitions.add(
 builder.column("name", "员工姓名").columnWidth(20)
 );
 for (int i = 0; i < 31; i++) {
 columnDefinitions.add(
 builder.<StaffAttendance.Attendance>column(String.format("attendances[%d]", i), String.valueOf(i + 1))
 .writeConvert((e, v) -> v.getText())
 // 把请假的标记为红色
 .cellStyleKey((c, e, v) -> v.equals(StaffAttendance.Attendance.LEAVE.getText()) ? "fg {color: RED}" : null)
 .columnWidth(3)
 );
 }
 // 可以用工具类方便计算出列名
 final String startColumnName = ColumnUtils.getColumnNameByIndex((short) 1); // B
 final String endColumnName = ColumnUtils.getColumnNameByIndex((short) 31); // AF
 // 居右的红色文字样式
 final CellStyleKeyProvider<StaffAttendance> redRightProvider = (c, e, v) -> "a {h: right} f {color: #ff0000}";
 // 使用函数进行统计 {row} 动态行数
 columnDefinitions.add(
 builder.formula(Integer.class, "出勤天数", String.format("=COUNTIF(%s{row}:%s{row},\"Y\")", startColumnName, endColumnName))
 .cellStyleKey(redRightProvider).columnWidth(10)
 );
 columnDefinitions.add(
 builder.formula(Integer.class, "请假天数", String.format("=COUNTIF(%s{row}:%s{row},\"N\")", startColumnName, endColumnName))
 .cellStyleKey(redRightProvider).columnWidth(10)
 );
 return columnDefinitions.toArray(ColumnDefinition[]::new);
 }
 })
 .writeToFile("D:\\员工考勤表.xlsx");

About

excel导入导出工具

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages

AltStyle によって変換されたページ (->オリジナル) /