学习参考文档
<dependencies>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>easyexcelartifactId>
<version>2.2.6version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<version>2.3.5.RELEASEversion>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.16version>
<scope>compilescope>
dependency>
dependencies>
// JavaBean
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
public class User {
@ExcelProperty(value = "用户编号")
private Integer userId;
@ExcelProperty(value = "姓名")
private String userName;
@ExcelProperty(value = "性别")
private String gender;
@ExcelProperty(value = "工资")
private Double salary;
@ExcelProperty(value = "入职时间")
private Date hireDate;
// lombok 会生成getter/setter方法
}
// 测试类
import com.alibaba.excel.EasyExcel;
import com.ublink.domain.User;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class test01 {
@Test
public void testWriteExcel() {
//没有文件的话就先去创建一下
String filename = "C:\\Users\\Admin\\Desktop\\user.xlsx";
// 向Excel中写入数据 也可以通过 head(Class>) 指定数据模板
EasyExcel.write(filename, User.class)
.sheet("用户信息")
.doWrite(getUserData());
}
@Test
public void testWriteExcel2() {
String filename = "C:\\Users\\Admin\\Desktop\\user1.xlsx";
// 创建ExcelWriter对象
ExcelWriter excelWriter = EasyExcel.write(filename, User.class).build();
// 创建Sheet对象
WriteSheet writeSheet = EasyExcel.writerSheet("用户信息").build();
// 向Excel中写入数据
excelWriter.write(getUserData(), writeSheet);
// 关闭流
excelWriter.finish();
}
// 根据user模板构建数据
private List<User> getUserData() {
List<User> users = new ArrayList<User>();
for (int i = 1; i <= 10; i++) {
User user = User.builder()
.userId(i)
.userName("admin" + i)
.gender(i % 2 == 0 ? "男" : "女")
.salary(i * 1000.00)
.hireDate(new Date())
.build();
users.add(user);
}
return users;
}
}
一般来说的话文件写入在找不到文件时会自动创建,防止意外的话直接手动创建就是了
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.read.builder.ExcelReaderBuilder;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.ublink.domain.User;
import org.junit.Test;
public class test02 {
@Test
// 在读取Excel表格数据时, 将读取的每行记录映射成一条LinkedHashMap记录, 而没有映射成实体类.
public void testRead() {
String filename = "C:\\Users\\Admin\\Desktop\\user.xlsx";
// 创建ExcelReaderBuilder对象
ExcelReaderBuilder readerBuilder = EasyExcel.read();
// 获取文件对象
readerBuilder.file(filename);
// 指定映射的数据模板
// readerBuilder.head(DemoData.class);
// 指定sheet
readerBuilder.sheet(0);
// 自动关闭输入流
readerBuilder.autoCloseStream(true);
// 设置Excel文件格式
readerBuilder.excelType(ExcelTypeEnum.XLSX);
// 注册监听器进行数据的解析
readerBuilder.registerReadListener(new AnalysisEventListener() {
// 每解析一行数据,该方法会被调用一次
@Override
public void invoke(Object demoData, AnalysisContext analysisContext) {
// 如果没有指定数据模板, 解析的数据会封装成 LinkedHashMap返回
// demoData instanceof LinkedHashMap 返回 true
System.out.println("解析数据为:" + demoData.toString());
}
// 全部解析完成被调用
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
System.out.println("解析完成...");
// 可以将解析的数据保存到数据库
}
});
readerBuilder.doReadAll();
/* // 构建读取器
ExcelReader excelReader = readerBuilder.build();
// 读取Excel
excelReader.readAll();
// 关闭流
excelReader.finish();*/
}
@Test
public void testReadExcel() {
// 读取的excel文件路径
String filename = "C:\\Users\\Admin\\Desktop\\user.xlsx";
// 读取excel
EasyExcel.read(filename, User.class, new AnalysisEventListener<User>() {
// 每解析一行数据,该方法会被调用一次
@Override
public void invoke(User user, AnalysisContext analysisContext) {
System.out.println("解析数据为:" + user.toString());
}
// 全部解析完成被调用
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
System.out.println("解析完成...");
// 可以将解析的数据保存到数据库
}
}).sheet().doRead();
}
@Test
public void testReadExcel2() {
// 读取的excel文件路径
String filename = "D:\\study\\excel\\read.xlsx";
// 创建一个数据格式来装读取到的数据
Class<DemoData> head = DemoData.class;
// 创建ExcelReader对象
ExcelReader excelReader = EasyExcel.read(filename, head, new AnalysisEventListener<DemoData>() {
// 每解析一行数据,该方法会被调用一次
@Override
public void invoke(DemoData demoData, AnalysisContext analysisContext) {
System.out.println("解析数据为:" + demoData.toString());
}
// 全部解析完成被调用
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
System.out.println("解析完成...");
// 可以将解析的数据保存到数据库
}
}).build();
// 创建sheet对象,并读取Excel的第一个sheet(下标从0开始), 也可以根据sheet名称获取
ReadSheet sheet = EasyExcel.readSheet(0).build();
// 读取sheet表格数据, 参数是可变参数,可以读取多个sheet
excelReader.read(sheet);
// 需要自己关闭流操作,在读取文件时会创建临时文件,如果不关闭,会损耗磁盘,严重的磁盘爆掉
excelReader.finish();
}
}
结果1:
这是由于我将两张表放在一个文件里,因此打印了两张表的数据
结果2:映射后的效果
@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
public class DemoData {
@ExcelProperty(value = "字符串标题", index = 0)
private String name;
@ExcelProperty(value = "日期标题", index = 1)
// 格式化日期类型数据
@DateTimeFormat(value = "yyyy年MM月dd日 HH时mm分ss秒")
private Date hireDate;
@ExcelProperty(value = "数字标题", index = 2)
// 格式化数字类型数据,保留一位小数
@NumberFormat(value = "###.#")
private String salary;
//注意: @NumberFormat对于Double类型的数据格式化会失效,建议使用String类型接收数据进行格式化
// private Double salary;
// lombok 会生成getter/setter方法
}
我手敲的路径,少了个s(手动捂脸),大家作为反面教材引以为戒哈
C:\Users\Admin\Desktop\user.xlsx
我这边猜测的可能性最大的是:
文件被打开了(比如看看学习成果)导致文件访问异常
字段不匹配:检查一下表格文件的表头字段是不是跟JavaBean属性名对应的
@Test
public void testWriteExcel3() {
String filename = "C:\\Users\\Admin\\Desktop\\user.xlsx";
// 设置排除的属性 也可以在数据模型的字段上加@ExcelIgnore注解排除
Set<String> excludeField = new HashSet<String>();
excludeField.add("hireDate");
excludeField.add("salary");
// 写Excel
EasyExcel.write(filename, User.class)
.excludeColumnFiledNames(excludeField)
.sheet("用户信息")
.doWrite(getUserData());
}
@Test
public void testWriteExcel4() {
String filename = "C:\\Users\\Admin\\Desktop\\user.xlsx";
// 设置要导出的字段
Set<String> includeFields = new HashSet<String>();
includeFields.add("userName");
includeFields.add("hireDate");
includeFields.add("salary");
// 写Excel
EasyExcel.write(filename, User.class)
.includeColumnFiledNames(includeFields)
.sheet("用户信息")
.doWrite(getUserData());
}
表头字段顺序按JavaBean顺序来,读出的时候按表格文件中的顺序来
修改后的JavaBean
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
public class User {
@ExcelProperty(value = "用户编号")
private Integer userId;
@ExcelProperty(value = "姓名", index = 1)
private String userName;
@ExcelProperty(value = "性别")
private String gender;
@ExcelProperty(value = "工资", index = 0)
private Double salary;
@ExcelProperty(value = "入职时间", index = 2)
private Date hireDate;
// lombok 会生成getter/setter方法
}
使用@DateTimeFormat()注解转化日期时间,value属性填写日期格式
使用@NumberFormat()注解转化数字,value写数字格式,#代替数字位置
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.annotation.format.NumberFormat;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
public class DemoData {
@ExcelProperty(value = "字符串标题", index = 1)
private String name;
@ExcelProperty(value = "日期标题", index = 2)
// 格式化日期类型数据
@DateTimeFormat(value = "yyyy年MM月dd日 HH时mm分ss秒")
private Date hireDate;
@ExcelProperty(value = "数字标题", index = 0)
// 格式化数字类型数据,保留一位小数
@NumberFormat(value = "###.#")
private String salary;
//注意: @NumberFormat对于Double类型的数据格式化会失效,建议使用String类型接收数据进行格式化
// private Double salary;
// lombok 会生成getter/setter方法
}
测试类进行读取
@Test
public void testRead() {
String filename = "C:\\Users\\Admin\\Desktop\\user.xlsx";
// 创建ExcelReaderBuilder对象
ExcelReaderBuilder readerBuilder = EasyExcel.read();
// 获取文件对象
readerBuilder.file(filename);
// 指定映射的数据模板
// readerBuilder.head(DemoData.class);
// 指定sheet
readerBuilder.sheet(0);
// 自动关闭输入流
readerBuilder.autoCloseStream(true);
// 设置Excel文件格式
readerBuilder.excelType(ExcelTypeEnum.XLSX);
// 注册监听器进行数据的解析
readerBuilder.registerReadListener(new AnalysisEventListener() {
// 每解析一行数据,该方法会被调用一次
@Override
public void invoke(Object demoData, AnalysisContext analysisContext) {
// 如果没有指定数据模板, 解析的数据会封装成 LinkedHashMap返回
// demoData instanceof LinkedHashMap 返回 true
System.out.println("解析数据为:" + demoData.toString());
}
// 全部解析完成被调用
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
System.out.println("解析完成...");
// 可以将解析的数据保存到数据库
}
});
readerBuilder.doReadAll();
/* // 构建读取器
ExcelReader excelReader = readerBuilder.build();
// 读取Excel
excelReader.readAll();
// 关闭流
excelReader.finish();*/
}
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.ContentRowHeight;
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
@HeadRowHeight(value = 30) // 头部行高
@ContentRowHeight(value = 25) // 内容行高
@ColumnWidth(value = 20) // 列宽
public class WidthAndHeightData {
@ExcelProperty(value = "字符串标题")
private String string;
@ExcelProperty(value = "日期标题")
private Date date;
@ExcelProperty(value = "数字标题")
@ColumnWidth(value = 25)
private Double doubleData;
// lombok 会生成getter/setter方法
}
import com.alibaba.excel.EasyExcel;
import com.ublink.domain.WidthAndHeightData;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class Test03 {
@Test
public void testWrite11() {
String filename = "C:\Users\Admin\Desktop\userwh.xlsx";
// 构建数据
List<WidthAndHeightData> dataList = new ArrayList<WidthAndHeightData>();
WidthAndHeightData data = WidthAndHeightData.builder()
.string("字符串")
.date(new Date())
.doubleData(888.88)
.build();
dataList.add(data);
// 向Excel中写入数据
EasyExcel.write(filename, WidthAndHeightData.class)
.sheet("行高和列宽测试")
.doWrite(dataList);
}
}
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.poi.ss.usermodel.FillPatternType;
import java.util.Date;
@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
@HeadRowHeight(value = 30) // 头部行高
@ContentRowHeight(value = 25) // 内容行高
@ColumnWidth(value = 20) // 列宽
// 头背景设置成红色 IndexedColors.RED.getIndex()
@HeadStyle(fillPatternType = FillPatternType.SOLID_FOREGROUND, fillForegroundColor = 10)
// 头字体设置成20, 字体默认宋体
@HeadFontStyle(fontName = "宋体", fontHeightInPoints = 20)
// 内容的背景设置成绿色 IndexedColors.GREEN.getIndex()
@ContentStyle(fillPatternType = FillPatternType.SOLID_FOREGROUND, fillForegroundColor = 17)
// 内容字体设置成20, 字体默认宋体
@ContentFontStyle(fontName = "宋体", fontHeightInPoints = 20)
public class DemoStyleData {
// 字符串的头背景设置成粉红 IndexedColors.PINK.getIndex()
@HeadStyle(fillPatternType = FillPatternType.SOLID_FOREGROUND, fillForegroundColor = 14)
// 字符串的头字体设置成20
@HeadFontStyle(fontHeightInPoints = 30)
// 字符串的内容背景设置成天蓝 IndexedColors.SKY_BLUE.getIndex()
@ContentStyle(fillPatternType = FillPatternType.SOLID_FOREGROUND, fillForegroundColor = 40)
// 字符串的内容字体设置成20,默认宋体
@ContentFontStyle(fontName = "宋体", fontHeightInPoints = 20)
@ExcelProperty(value = "字符串标题")
private String string;
@ExcelProperty(value = "日期标题")
private Date date;
@ExcelProperty(value = "数字标题")
private Double doubleData;
// lombok 会生成getter/setter方法
}
@Test
public void testWrite12() {
String filename = "C:\Users\Admin\Desktop\user12.xlsx";
// 构建数据
List<DemoStyleData> dataList = new ArrayList<>();
DemoStyleData data = DemoStyleData.builder()
.string("字符串")
.date(new Date())
.doubleData(888.88)
.build();
dataList.add(data);
// 向Excel中写入数据
EasyExcel.write(filename, DemoStyleData.class)
.sheet("样式设置测试")
.doWrite(dataList);
}
@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
@HeadRowHeight(value = 25) // 头部行高
@ContentRowHeight(value = 20) // 内容行高
@ColumnWidth(value = 20) // 列宽
/**
* @OnceAbsoluteMerge 指定从哪一行/列开始,哪一行/列结束,进行单元格合并
* firstRowIndex 起始行索引,从0开始
* lastRowIndex 结束行索引
* firstColumnIndex 起始列索引,从0开始
* lastColumnIndex 结束列索引
*/
// 例如: 第2-3行,2-3列进行合并
@OnceAbsoluteMerge(firstRowIndex = 1, lastRowIndex = 2, firstColumnIndex = 1, lastColumnIndex = 2)
public class DemoMergeData {
// 每隔两行合并一次(竖着合并单元格)
// @ContentLoopMerge(eachRow = 2)
@ExcelProperty(value = "字符串标题")
private String string;
@ExcelProperty(value = "日期标题")
private Date date;
@ExcelProperty(value = "数字标题")
private Double doubleData;
// lombok 会生成getter/setter方法
}
@Test
public void testWrite13() {
String filename = "C:\Users\Admin\Desktop\user13.xlsx";
// 构建数据
List<DemoMergeData> dataList = new ArrayList<>();
DemoMergeData data = DemoMergeData.builder()
.string("字符串")
.date(new Date())
.doubleData(888.88)
.build();
dataList.add(data);
// 向Excel中写入数据
EasyExcel.write(filename, DemoMergeData.class)
.sheet("单元格合并测试")
.doWrite(dataList);
}