目录
第一章 引入相关依赖
第二章 导出数据进入excel
2.1 导出的简单实现
2.1.1 创建导出实体类dto
2.1.2 第一种实现方式
2.1.3 第二种实现方式
2.2 导出的扩展
2.2.1 将类中某几个字段排除掉,不写入Excel中
2.2.2 只向表格中导出指定某几个字段
2.2.3 设定导出的列顺序
2.2.4 复杂头数据写入(多级列表头)
2.2.5 重复写到Excel的同一个sheet中
2.2.6 重复的写到Excel的不同sheet中
2.2.7 日期、数字类型格式化
2.2.8 写入图片到Excel
2.2.9 设置写入Excel的列宽行高设置
2.2.10 通过注解形式设置写入Excel的样式(字体字号,填充颜色等)
2.2.11 合并单元格
第三章 从Excel中读取数据
3.1两种简单的方法实现读取
3.1.1 实现方式一
3.1.2 实现方式二
3.2 Excel读取的扩展
3.2.1 根据Excel中指定的列名或者列的下标读取指定列的数据
3.2.2 格式化Excel中的数据格式(例如时间)
3.2.3 读取多个或者全部的Sheet
第四章 填充Excel
4.1 简单填充(单个对象填充)
4.2 列表填充(多记录填充)
第五章 Web操作(Excel上传/下载)
5.1 引入相关依赖
5.2 html设计
5.3 实现下载
5.4 实现上传
使用maven管理项目,引入EasyExcel依赖
com.alibaba
easyexcel
2.2.3
junit
junit
4.12
test
org.projectlombok
lombok
1.18.22
首先创建需要使用的dto,用于设置导出使用的模板类;
使用easyExcel提供的注解@ExcelProperty标记字段
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
@ExcelProperty("用户编号")
private Integer userId;
@ExcelProperty("姓名")
private String userName;
@ExcelProperty("性别")
private String gender;
@ExcelProperty("工资")
private Double salary;
@ExcelProperty("入职时间")
private Date hireDate;
}
@Test
public void test1() {
String fileNameAndLocation = "E:\\xx\\xxx\\easyExcel\\src\\main\\resources\\user1.xlsx";
//User是设定好的dto,专门用来做导出。使用EasyExcel的相关标签设置了表头等
List users = new ArrayList<>();
User user1 = new User(101, "李四", "男", 10000.09, new Date());
User user2 = new User(102, "小美", "女", 10033.09, new Date());
users.add(user1);
users.add(user2);
//将数据写到excel的第一个sheet标签中,并且给sheet标签起名字
//会自动在设定的位置创建excel
EasyExcel.write(fileNameAndLocation, User.class)
.sheet("用户信息")
.doWrite(users);
//不用关闭流资源,执行完后,会自动关闭文件流
}
@Test
public void test2() {
//创建文件保存的位置,以及文件名
String fileNameAndLocation = "E:\\xx\\xxx\\easyExcel\\src\\main\\resources\\user2.xlsx";
/**
* 构建要写入的数据
* User类是一个自定义的特殊类,专门用来构建向Excel中写数据的类型类
* @ExcelProperty是easyexcel提供的注解,用来定义表格的头部
*/
List data = new ArrayList<>();
User user = new User(2001, "李雷", "男", 1000.123, new Date());
data.add(user);
//创建Excel写对象
ExcelWriter excelWriter = EasyExcel.write(fileNameAndLocation, User.class).build();
//创建sheet对象
WriteSheet writeSheet = EasyExcel.writerSheet("用户信息").build();
//将数据写到sheet标签中
excelWriter.write(data, writeSheet);
//关闭流,文件流手动关闭
excelWriter.finish();
}
@Test
public void test3() {
//创建保存位置以及文件名
String fileNameAndLocation = "E:\\xx\\easyExcel\\src\\main\\resources\\user3.xlsx";
List data = new ArrayList<>();
User user = new User(2001, "李雷", "男", 1000.123, new Date());
data.add(user);
//构建要排除导出的列
Set excludeColumnFIelds = new HashSet<>();
excludeColumnFIelds.add("hireDate");
excludeColumnFIelds.add("userName");
EasyExcel.write(fileNameAndLocation, User.class)
.excludeColumnFiledNames(excludeColumnFIelds)
.sheet("用户信息表(排除字段后)")
.doWrite(data);
}
@Test
public void test4() {
//创建保存位置以及文件名
String fileNameAndLocation = "E:\\xx\\easyExcel\\src\\main\\resources\\user4.xlsx";
List data = new ArrayList<>();
User user = new User(2001, "李雷", "男", 1000.123, new Date());
data.add(user);
//构建要导出的列
Set includeColumnFIelds = new HashSet<>();
includeColumnFIelds.add("hireDate");
includeColumnFIelds.add("userName");
EasyExcel.write(fileNameAndLocation, User.class)
.includeColumnFiledNames(includeColumnFIelds)
.sheet("用户信息表(设定的字段)")
.doWrite(data);
}
创建新的dto,类名为IndexUser;
与之前相比标签内多了一个属性index,该参数从0开始,标识该字段作为excel中第几列出现;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class IndexUser {
/*
* value属性:用来设置该字段对应表格中的表头
* index属性:用来设置列的顺序,也就是设定该字段为表格中的第几列
* */
@ExcelProperty(value = "用户姓名",index = 0)
private String userName;
@ExcelProperty(value = "用户编号",index = 2)
private Integer userId;
@ExcelProperty(value = "入职时间",index = 1)
private Date hireDate;
}
@Test
public void test5() {
String fileNameAndLocation = "E:\\xx\\easyExcel\\src\\main\\resources\\user5.xlsx";
List data = new ArrayList<>();
IndexUser indexUser = new IndexUser("李四", 1001, new Date());
data.add(indexUser);
EasyExcel.write(fileNameAndLocation, IndexUser.class).sheet("用户信息表").doWrite(data);
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ComplexHeadUser {
/**
* {"用户主题1","用户编号"}这种遇到格式设置多级主题
* 第一个参数为一级主题(第一行)
* 第二个参数为二级主题(第二行)
* 如果一级主题名称相同,那么他们会合并单元格
*
* 注意:要是想合并单元格,要求一级主题相邻(有相同一级主题的字段声明时紧挨着),
或者使用index参数设定需要合并单元格的字段相邻
*/
@ExcelProperty(value = {"用户主题1", "用户编号"},index = 0)
private Integer userId;
@ExcelProperty(value = {"用户主题2", "用户名称"},index = 2)
private String userName;
@ExcelProperty(value = {"用户主题1", "入职时间"},index = 1)
private Date hireDate;
}
@Test
public void test6() {
//创建文件保存的位置,以及文件名
String fileNameAndLocation = "E:\\xx\\easyExcel\\src\\main\\resources\\user6.xlsx";
//构建要写入的数据
List data = new ArrayList<>();
ComplexHeadUser user = new ComplexHeadUser(1001, "丽丽", new Date());
data.add(user);
EasyExcel.write(fileNameAndLocation, ComplexHeadUser.class).sheet("abc").doWrite(data);
}
效果图如下:
@Test
public void test7() {
//创建文件保存的位置,以及文件名
String fileNameAndLocation = "E:\\xx\\easyExcel\\src\\main\\resources\\user7.xlsx";
//构建数据
List data = new ArrayList<>();
User user1 = new User(2001, "李雷1", "男", 1000.123, new Date());
User user2 = new User(2002, "李雷2", "男", 1000.123, new Date());
User user3 = new User(2003, "李雷3", "男", 1000.123, new Date());
User user4 = new User(2004, "李雷4", "男", 1000.123, new Date());
data.add(user1);
data.add(user2);
data.add(user3);
data.add(user4);
//使用第二种方法导出
ExcelWriter excelWriter = EasyExcel.write(fileNameAndLocation, User.class).build();
WriteSheet writeSheet = EasyExcel.writerSheet("用户信息表").build();
for (int i = 0; i < 5; i++) {
excelWriter.write(data, writeSheet);
}
//关闭文件流
excelWriter.finish();
}
@Test
public void test8() {
//创建文件保存的位置,以及文件名
String fileNameAndLocation = "E:\\xx\\easyExcel\\src\\main\\resources\\user8.xlsx";
//构建数据
List data = new ArrayList<>();
User user1 = new User(2001, "李雷1", "男", 1000.123, new Date());
User user2 = new User(2002, "李雷2", "男", 1000.123, new Date());
User user3 = new User(2003, "李雷3", "男", 1000.123, new Date());
User user4 = new User(2004, "李雷4", "男", 1000.123, new Date());
data.add(user1);
data.add(user2);
data.add(user3);
data.add(user4);
//创建ExcelWriter对对象并设置用哪个Class(数据类型)去写
ExcelWriter excelWriter = EasyExcel.write(fileNameAndLocation, User.class).build();
//模拟重复写入,模拟重复写入5次
for (int i = 0; i < 5; i++) {
//创建sheet(因为写入到不同的sheet,所以需要多次创建sheet)
//WriteSheet writeSheet = EasyExcel.writerSheet(i,"用户信息"+i).build(); //用下面的格式也可以
WriteSheet writeSheet = EasyExcel.writerSheet("用户信息" + i).build();
excelWriter.write(data, writeSheet);
}
//关闭文件流
excelWriter.finish();
}
需要在设置dto的时候使用下面两种注解 @DateTimeFormat : 日期格式化 @NumberFormat : 数字格式化(小数或者百分数)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
/**
* 用户id
*/
@ExcelProperty("用户编号")
private Integer userId;
/**
* 用户名
*/
@ExcelProperty("姓名")
private String userName;
/**
* 性别
*/
@ExcelProperty("性别")
private String gender;
/**
* 工资
* 格式化小数类型,如果是百分数那么定义为 #.##% 比如9.12%
*/
@NumberFormat("#,###.##")
@ExcelProperty("工资")
private Double salary;
/**
* 雇佣日期
*/
@DateTimeFormat("yyyy年MM月dd日 HH时mm分ss秒")
@ExcelProperty("入职时间")
private Date hireDate;
}
@Test
public void test9() {
//创建文件保存的位置,以及文件名
String fileNameAndLocation = "E:\\xx\\easyExcel\\src\\main\\resources\\user9.xlsx";
//构建数据
List data = new ArrayList<>();
User user1 = new User(2001, "李雷1", "男", 100000000.12223, new Date());
User user2 = new User(2002, "李雷2", "男", 1000.12223, new Date());
User user3 = new User(2003, "李雷3", "男", 1000.12223, new Date());
User user4 = new User(2004, "李雷4", "男", 1000.13323, new Date());
data.add(user1);
data.add(user2);
data.add(user3);
data.add(user4);
EasyExcel.write(fileNameAndLocation, User.class).sheet("用戶信息").doWrite(data);
}
效果图如下:
向Excel导入图片(一共五种方式) 1. 图片可以是一个File格式 2. 图片可以是一个InputStream输入流的方式 3. 图片可以是一个byte[]数组的方式 4. 图片可以是 一个网络的java.net.URL对象方式 5. 图片也可以是一个String类型方式(当是String类型是,需要StringImageConverter类型转换器)下面分别用这5种方式实现写入图片进入Excel中,实际开发中选择一种适合即可。下面设计dto的时候使用的两个设置列表格式的注解将在后面提到。
@Data
@AllArgsConstructor
@NoArgsConstructor
@ContentRowHeight(100) // 内容高度
@ColumnWidth(100 / 4) // 列宽
public class ImageData {
// 使用抽象文件表示一个图片
@ExcelProperty(value = "文件保存")
private File file;
// 使用输入流保存一个图片
@ExcelProperty(value = "输入流保存")
private InputStream inputStream;
//当使用String类型保存一个图片的时候需要使用StringImageConverter转换器
@ExcelProperty(converter = StringImageConverter.class,value = "String保存")
private String string;
// 使用二进制数据保存为一个图片
@ExcelProperty("二进制数据保存")
private byte[] byteArray;
// 使用网络链接保存一个图片
@ExcelProperty(value = "网络url保存")
private URL url;
}
@Test
public void test10() throws IOException {
//创建文件保存的位置,以及文件名
String fileNameAndLocation = "E:\\xx\\easyExcel\\src\\main\\resources\\user10.xlsx";
//构建数据
ArrayList dataArrayList = new ArrayList<>();
ImageData imageData = new ImageData();
//方式一:通过文件保存一个图片
imageData.setFile(new File("E:\\xx\\easyExcel\\src\\main\\resources\\img1.png"));
//方式二:通过输入流保存一个图片
imageData.setInputStream(new FileInputStream(new File("E:\\xx\\easyExcel\\src\\main\\resources\\img1.png")));
//方式三:通过String类型保存一个图片
imageData.setString("E:\\xx\\easyExcel\\src\\main\\resources\\img1.png");
//方式四:通过url网络连接
imageData.setUrl(new URL("https://img-blog.csdnimg.cn/bb77c2f817eb4d698f7a23a41ba8f5a0.png"));
//方式五:通过二进制数据保存一个图片
int length = (int) new File("E:\\xx\\easyExcel\\src\\main\\resources\\img1.png").length();
byte[] bytes = new byte[length];
FileInputStream inputStream = new FileInputStream("E:\\xx\\easyExcel\\src\\main\\resources\\img1.png");
inputStream.read(bytes, 0, length);
imageData.setByteArray(bytes);
dataArrayList.add(imageData);
//写数据
EasyExcel.write(fileNameAndLocation, ImageData.class).sheet("图片信息").doWrite(dataArrayList);
//关闭流
inputStream.close();
}
实际的效果图如下展示 :
@Data
@AllArgsConstructor
@NoArgsConstructor
@ContentRowHeight(30) //设置内容单元格高度
@HeadRowHeight(40) //设置表头单元格高度
@ColumnWidth(25) //内容单元格宽度和内容单元格宽度是一样的,只有一个注解
public class WidthAndHeightData {
@ExcelProperty("字符串标题")
private String string;
@ExcelProperty("日期标题")
private Date date;
@ColumnWidth(20) //设置列宽(可以修饰类,也可以修饰具体属性)
@ExcelProperty("数字标题")
private Double doubleData;
}
@Test
public void test11() {
//创建文件保存的位置,以及文件名
String fileNameAndLocation = "E:\\xx\\easyExcel\\src\\main\\resources\\user11.xlsx";
//构建数据
List list = new ArrayList();
WidthAndHeightData wd = new WidthAndHeightData();
wd.setString("红猫");
wd.setDoubleData(200.12);
wd.setDate(new Date());
list.add(wd);
//写数据
EasyExcel.write(fileNameAndLocation, WidthAndHeightData.class).sheet("信息").doWrite(list);
}
效果图如下所示。
//头背景设置成红色 IndexedColors.RED.getIndex()
@HeadStyle(fillPatternType = FillPatternType.SOLID_FOREGROUND, fillForegroundColor = 10)
//内容的背景设置成绿色 IndexedColors.GREEN.getIndex()
@ContentStyle(fillPatternType = FillPatternType.SOLID_FOREGROUND, fillForegroundColor = 17)
//表头单元格 字体设置为宋体10号
@HeadFontStyle(fontHeightInPoints = 10,fontName = "宋体")
//内容字体设置成10号
@ContentFontStyle(fontHeightInPoints = 10)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class StyleColumnData {
// 字符串的头背景设置成粉红 IndexedColors.PINK.getIndex()
@HeadStyle(fillPatternType = FillPatternType.SOLID_FOREGROUND, fillForegroundColor = 14)
// 字符串的内容的背景设置成天蓝 IndexedColors.SKY_BLUE.getIndex()
@ContentStyle(fillPatternType = FillPatternType.SOLID_FOREGROUND, fillForegroundColor = 40)
// 字符串的头字体设置成11
@HeadFontStyle(fontHeightInPoints = 11)
// 字符串的内容字体设置成11
@ContentFontStyle(fontHeightInPoints = 11)
@ExcelProperty("字符串标题")
private String string;
@ExcelProperty("日期标题")
private Date date;
@ExcelProperty("数字标题")
private Double doubleData;
}
/**
* 通过注解形式设置写入Excel的样式,字体字号,背景填充颜色
*/
@Test
public void test12() {
//创建文件保存的位置,以及文件名
String fileNameAndLocation = "E:\\xx\\easyExcel\\src\\main\\resources\\user12.xlsx";
//构建数据
List list = new ArrayList<>();
StyleColumnData scd = new StyleColumnData();
scd.setDate(new Date());
scd.setDoubleData(200.12);
scd.setString("小美美");
list.add(scd);
EasyExcel.write(fileNameAndLocation, StyleColumnData.class).sheet("信息").doWrite(list);
}
具体效果如下。
- 使用注解@OnceAbsoluteMerge设置列或者行的合并,根据行得两个绝对行号和列的两个绝对列号,开始的index都是0,指代第一行或者第一列。这个主机在实体类的上面标注。
- 注解@ContentLoopMerge使用在属性的上面,表示竖着循环合并单元格。如下面表示每两行合并一次,并且每两个中的第一个作为合并的内容,就是说当前两行内容分别是a,b,合并后的内容显示的是a。
@Data
@AllArgsConstructor
@NoArgsConstructor
@OnceAbsoluteMerge(firstRowIndex = 1,lastRowIndex = 2,firstColumnIndex = 1,lastColumnIndex = 2)
public class MergeData {
//每隔两行合并一次(竖着合并单元格)
@ContentLoopMerge(eachRow = 2)
@ExcelProperty("字符串标题")
private String string;
@ExcelProperty("日期标题")
private Date date;
@ExcelProperty("数字标题")
private Double doubleData;
}
@Test
public void test13() {
//创建文件保存的位置,以及文件名
String fileNameAndLocation = "E:\\xx\\easyExcel\\src\\main\\resources\\user13.xlsx";
//构建数据
List list = new ArrayList<>();
MergeData dmd = new MergeData();
dmd.setDate(new Date());
dmd.setDoubleData(200.12);
dmd.setString("双面龟");
MergeData dmd2 = new MergeData();
dmd2.setDate(new Date());
dmd2.setDoubleData(200.12);
dmd2.setString("小美美");
MergeData dmd3 = new MergeData();
dmd3.setDate(new Date());
dmd3.setDoubleData(200.12);
dmd3.setString("小美美");
MergeData dmd4 = new MergeData();
dmd4.setDate(new Date());
dmd4.setDoubleData(200.12);
dmd4.setString("双面龟");
MergeData dmd5 = new MergeData();
dmd5.setDate(new Date());
dmd5.setDoubleData(200.12);
dmd5.setString("双面龟");
list.add(dmd);
list.add(dmd2);
list.add(dmd3);
list.add(dmd4);
list.add(dmd5);
EasyExcel.write(fileNameAndLocation, MergeData.class).sheet("信息sheet").doWrite(list);
}
效果图如下。
设定需要读取的文件Excel如下
注意下面这种方式,没有设置对应关系,所以每个属性要和Excel中的字段对应上。顺序不能错。
@Data
@AllArgsConstructor
@NoArgsConstructor
public class DemoData {
private String name;
private Date hireDate;
private Double salary;
}
.sheet()里面不设置读取的对应的sheet名的话或者sheet对应的index(从0开始),默认是读取第一个sheet
@Test
public void test2_0() {
//读取的文件路径(需要读取的Excel的位置)
String fileNameAndLocation = "E:\\xx\\easyExcel\\src\\main\\resources\\user2_0.xlsx";
//创用来接收Excel表格对应数据的实体类
Class head = DemoData.class;
//读取数据
EasyExcel.read(fileNameAndLocation, head, new AnalysisEventListener() {
//解析每一条数据的时候被调用
@Override
public void invoke(DemoData data, AnalysisContext context) {
//在这里操作,将解析的每一条数据保存到数据库中,在这里可以调用数据库(比如将数据导入库中)
System.out.println("解析的数据为: " + data);
}
//解析完所有数据的时候被调用
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
System.out.println("数据解析完成..........");
}
}).sheet().doRead();
}
@Test
public void test2_1() {
//读取的文件路径
String fileNameAndLocation = "E:\\xx\\easyExcel\\src\\main\\resources\\user2_1.xlsx";
//创建接收Excel表格数据的实体类
Class head = DemoData.class;
//创建ExcelReader对象
ExcelReader excelReader = EasyExcel.read(fileNameAndLocation, head, new AnalysisEventListener() {
@Override
public void invoke(DemoData data, AnalysisContext context) {
System.out.println("读取到的数据为:" + data);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
System.out.println("数据解析已完成");
}
}).build();
//创建sheet对象,并读取Excel的第1个sheet(下标从0开始)
ReadSheet readSheet = EasyExcel.readSheet(0).build();
excelReader.read(readSheet);
//关闭流操作,在读取文件时会创建临时文件,如果不关闭,磁盘爆掉
excelReader.finish();
}
下面是对应读取excel中数据的实体类 两种匹配excel字段的方式: ①使用表头名name来匹配excel和实体类中的属性 ②使用index来匹配当前属性对应excel中的列位置字段 这里不建议 index 和 name 同时用,要么一个对象只用index,要么一个对象只用name去匹配。
@Data
@AllArgsConstructor
@NoArgsConstructor
public class IndexOrNameData {
/**
* 强制读取第4个
*/
@ExcelProperty(index = 3)
private Double doubleData;
/**
* 用名字去匹配,这里需要注意,如果名字重复,会导致只有一个字段读取到数据
*/
@ExcelProperty("用户编号")
private String string;
@ExcelProperty("入职时间")
private Date date;
}
@Test
public void test21() {
//设定读取的excel位置
String fileNameAndLocation = "E:\\xx\\easyExcel\\src\\main\\resources\\user21.xlsx";
//创建的实体类,来接收对应表格中的数据
Class head = IndexOrNameData.class;
//读取数据
EasyExcel.read(fileNameAndLocation, head, new AnalysisEventListener() {
//解析每一条数据的时候被调用
@Override
public void invoke(IndexOrNameData data, AnalysisContext context) {
//在这里操作,将解析的每一条数据保存到数据库中,在这里可以调用数据库
System.out.println("解析的数据为: " + data);
}
//解析完所有数据的时候被调用
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
System.out.println("数据解析完成..........");
}
}).sheet().doRead();
}
设定的字段会根据名称或者index,对应上属性和excel中的字段。然后根据使用的注解设定的格式去接收excel中的数据,并将匹配的数据存储在对应的实体类属性中。
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ConverterData {
@NumberFormat("#.#")
@ExcelProperty("数字标题")
private String salary;
@DateTimeFormat("yyyy-MM-dd HH:mm:ss")
@ExcelProperty("日期标题")
private Date hireDate;
}
@Test
public void test22() {
//读取的文件路径
String fileNameAndLocation = "E:\\xx\\easyExcel\\src\\main\\resources\\user22.xlsx";
//创建来接收表格数据的实体类
Class head = ConverterData.class;
//读取数据
EasyExcel.read(fileNameAndLocation, head, new AnalysisEventListener() {
/**
* 解析每一条数据的时候被调用
*/
@Override
public void invoke(ConverterData data, AnalysisContext context) {
//在这里操作,将解析的每一条数据保存到数据库中,在这里可以调用数据库
System.out.println("解析的数据为: " + data);
}
/**
* 解析完所有数据的时候被调用
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
System.out.println("数据解析完成..........");
}
}).sheet().doRead();
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class DemoData {
private String name;
private Date hireDate;
private Double salary;
}
@Test
public void test23() {
//读取的文件路径
String fileName = "user11.xlsx";
//创建的一个接收excel数据的实体类
Class head = DemoData.class;
/**
* 读取全部sheet(这种方式要求每个sheet页的内容都能用同一个实体类接收)
*/
EasyExcel.read(fileName, head, new AnalysisEventListener() {
@Override
public void invoke(DemoData data, AnalysisContext context) {
// 每解析一条数据被调用一次
System.out.println("解析的数据为: " + data);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 数据解析完成之后被调用
System.out.println("数据解析完成......");
}
}).doReadAll();
System.out.println("============================================================");
/**
* 读取其中的某几个sheet(读取的每个sheet,分别处理)
*/
//构建ExcelReader对象
ExcelReader excelReader = EasyExcel.read(fileName).build();
//创建想要获取的sheet对象
ReadSheet sheet1 = EasyExcel.readSheet(0).head(DemoData.class).registerReadListener(new AnalysisEventListener() {
@Override
public void invoke(DemoData data, AnalysisContext context) {
// 每解析一条数据被调用一次
System.out.println("解析的数据为: " + data);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 数据解析完成之后被调用
System.out.println("数据解析完成......");
}
}).build();
ReadSheet sheet2 = EasyExcel.readSheet(2).head(DemoData.class).registerReadListener(new AnalysisEventListener() {
@Override
public void invoke(DemoData data, AnalysisContext context) {
// 每解析一条数据被调用一次
System.out.println("解析的数据为: " + data);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 数据解析完成之后被调用
System.out.println("数据解析完成......");
}
}).build();
//读取sheet
excelReader.read(sheet1, sheet2);
//关闭
excelReader.finish();
}
不用使用注解提前标明实体类中的字段和excel中的对应关系。使用提前设置好的Excel模板接收数据
@Data
@AllArgsConstructor
@NoArgsConstructor
public class FillData {
private String name;
private Integer number;
}
- 这种方式只用来填充单条数据,完全可以用列表填充替代(下面说到)。
- 使用{}占位符来代表需要填充的数据,大括号里面的字段和用来填充的实体类中的属性名一致。
设定好的模板如下。
@Test
public void test24(){
//设置填充模板Excel
String templateFileName="E:\\xx\\easyExcel\\src\\main\\resources\\user22.xlsx";
//通过模板文件填充完毕后生成的文件
String fullFile="E:\\xx\\easyExcel\\src\\main\\resources\\fulledFile.xlsx";
//构建要填充的数据
FillData data1 = new FillData();
data1.setName("张三");
data1.setNumber(1001);
//填充操作
EasyExcel.write(fullFile).withTemplate(templateFileName).sheet().doFill(data1);
}
效果如下所示。
和单条数据填充使用的占位符相比,前面多加了一个“.”’,表示循环存储多条数据
设定的模板如下。
@Test
public void test25(){
//设置填充模板Excel
String templateFileName="E:\\xx\\easyExcel\\src\\main\\resources\\user23.xlsx";
//通过模板文件填充完毕后生成的文件
String fullFile="E:\\xx\\easyExcel\\src\\main\\resources\\fulledFile2.xlsx";
//构建要填充的数据
ArrayList arrayList = new ArrayList<>();
FillData data1 = new FillData();
FillData data2 = new FillData();
data1.setName("张三");
data1.setNumber(1001);
data2.setName("李四");
data2.setNumber(1002);
arrayList.add(data1);
arrayList.add(data2);
//填充操作
EasyExcel.write(fullFile).withTemplate(templateFileName).sheet().doFill(arrayList);
}
效果如图所示。
commons-fileupload
commons-fileupload
1.4
com.alibaba
easyexcel
2.2.3
org.springframework
spring-webmvc
5.3.1
ch.qos.logback
logback-classic
1.2.3
javax.servlet
javax.servlet-api
3.1.0
provided
org.thymeleaf
thymeleaf-spring5
3.0.12.RELEASE
使用theymeleaf来渲染页面(这里不需要关注,只需要能看懂如何实现就可以)
测试easyExcel上传和下载
进入测试页面
下载
前端页面显示如下。
设计dto如下。
@Data
@AllArgsConstructor
@NoArgsConstructor
public class EasyUser {
@ExcelProperty("用户编号")
private Integer userId;
@ExcelProperty("用户名称")
private String userName;
@ExcelProperty("用户性别")
private String gender;
@ExcelProperty("创建时间")
private Date createTime;
}
下面采用设定好的实体类来实现导出,根据需求也可以采用提前设定好的excel模板实现(前文提到)。
@GetMapping("/easyExcel/download")
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/**
* EasyExcel下载步骤
*/
//设置响应头(格式是固定的)
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
//设置下载的excel的文件名,并设置防止文件名中文乱码
String fileName = URLEncoder.encode("设置的文件名", "utf-8");
//文件下载方式(附件下载还是在当前浏览器打开)。
// 要是在当前浏览器打开(或者说在当前页面打开)第二个参数中的attachment就改称inline
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
//构建写入到Excel中的数据(此数据可以从数据库中获取)
List users = new ArrayList<>();
EasyUser user1 = new EasyUser(1001, "李雷1", "男", new Date());
EasyUser user2 = new EasyUser(1002, "李雷2", "男", new Date());
EasyUser user3 = new EasyUser(1003, "李雷3", "男", new Date());
users.add(user1);
users.add(user2);
users.add(user3);
EasyExcel.write(response.getOutputStream(), EasyUser.class).sheet("用户信息").doWrite(users);
}
点击下载后,效果如下,当前页面会有附件可以下载。
@PostMapping("/easyExcel/upload")
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload fileUpload = new ServletFileUpload(factory);
//设置单个文件为3M
fileUpload.setFileSizeMax(1024 * 1024 * 3);
//总文件大小为30M
fileUpload.setSizeMax(1024 * 1024 * 3 * 10);
try {
List list = fileUpload.parseRequest(req);
for (FileItem fileItem : list) {
//判断是否为附件
if (!fileItem.isFormField()) {
//是附件
InputStream inputStream = fileItem.getInputStream();
EasyExcel.read(inputStream, EasyUser.class, new AnalysisEventListener() {
@Override
public void invoke(EasyUser data, AnalysisContext arg1) {
//读完一行就执行一次(调用数据库进行插入操作等操作)
System.out.println("解析一行: " + data);
}
@Override
public void doAfterAllAnalysed(AnalysisContext arg0) {
System.out.println("Excel全部读完被执行......");
}
}).sheet().doRead();
} else {
//普通表单
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
//上传完成进行转发或者重定向
}
效果如下,选择文件后,点击上传。之后就会读取到文件,然后根据实际业务进行处理。