Alibaba Easy Excel快速入门
EasyExcel快速上手
一、Maven坐标
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.1.6</version>
</dependency>
二、常见问题
-
填充和写的选择
填充其实也不会占用大量内存,用的也是文件缓存,最后统一书写,如果导出的内容各种格式复杂,建议直接用模板然后填充(填充的数据会自动有格式)。如果格式相对简单,建议直接用写,相对来说,直接导出性能还是高一丢丢。
-
部分字段读取或者写入为空
读写反射对象用到了
Cglib,所以成员变量必须符合驼峰规范,而且使用@Data不能使用@Accessors(chain = true)。后续会考虑支持非驼峰。 -
其他问题参考官方文档。
三、读Excel
1. 最简单的读
-
对象
@Data public class Student { private String stu_name; private Integer age; private String address; private Float money; } -
表格(Excel)
姓名 年龄 地址 余额 张三 22 广东省深圳市 6543 李四 18 湖北省武汉市 4900 王五 36 云南省昆明市 5200 -
监听器
监听器采用匿名内部类的形式提供。
-
代码
@Test public void testRead() { // 被读取的文件绝对路径 String fileName = "C:/location/myFiles/temp/students.xlsx"; // 接收解析出的目标对象(Student) List<Student> studentList = new ArrayList<>(); // 这里需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭 // excel中表的列要与对象的字段相对应 EasyExcel.read(fileName, Student.class, new AnalysisEventListener<Student>() { // 每解析一条数据都会调用该方法 @Override public void invoke(Student student, AnalysisContext analysisContext) { System.out.println("解析一条Student对象:" + JSON.toJSONString(student)); studentList.add(student); } // 解析完毕的回调方法 @Override public void doAfterAllAnalysed(AnalysisContext analysisContext) { System.out.println("excel文件读取完毕!"); } }).sheet().doRead(); }
2. 指定列的下标或列名
读的方法都一样(包括监听器),只不过要给目标对象的类上加如下注解(demo来自官方文档)。
@Data
public class IndexOrNameData {
/**
* 强制读取第三个 这里不建议 index 和 name 同时用,要么一个对象只用index,要么一个对象只用name * 去匹配
*/
@ExcelProperty(index = 2)
private Double doubleData;
/**
* 用名字去匹配,这里需要注意,如果名字重复,会导致只有一个字段读取到数据
*/
@ExcelProperty("字符串标题")
private String string;
@ExcelProperty("日期标题")
private Date date;
}
3. 不创建对象的读
不创建对象读取的话,直接用Map接收数据。
@Test
public void readWithoutObj() {
// 被读取的文件绝对路径
String fileName = "C:/location/myFiles/temp/students.xlsx";
// 接收结果集,为一个List列表,每个元素为一个map对象,key-value对为excel中每个列对应的值
List<Map<Integer,String>> resultList = new ArrayList<>();
EasyExcel.read(fileName, new AnalysisEventListener<Map<Integer,String>>() {
@Override
public void invoke(Map<Integer, String> map, AnalysisContext analysisContext) {
System.out.println("解析到一条数据:" + JSON.toJSONString(map));
resultList.add(map);
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
System.out.println("excel文件解析完毕!" + JSON.toJSONString(resultList));
}
}).sheet().doRead();
}
4. 其他复杂方式读
读取多个sheet、日期/数字和自定义格式转换、多行头、同步返回、读取表头、web中的读等读法参考官方文档
四、写Excel
1. 最简单的写
@Test
public void testWrite() {
// 测试写
String writePath = "C:\\location\\myFiles\\temp\\students_write.xlsx";
Student student_1 = new Student("小明",22,"四川省雅安市",4800f);
Student student_2 = new Student("小张",19,"河南省郑州市",5100f);
Student student_3 = new Student("小吴",26,"陕西省西安市",6500f);
List<Student> studentList = new ArrayList<>();
studentList.add(student_1);
studentList.add(student_2);
studentList.add(student_3);
EasyExcel.write(writePath,Student.class).sheet().doWrite(studentList);
}
2. 根据参数写(排除)指定的列
/**
* 根据参数只写入(或排除)指定的列
*/
@Test
public void writeIncludeOrExclude() {
Student student_1 = new Student("小明",22,"四川省雅安市",4800f);
Student student_2 = new Student("小张",19,"河南省郑州市",5100f);
Student student_3 = new Student("小吴",26,"陕西省西安市",6500f);
List<Student> studentList = new ArrayList<>();
studentList.add(student_1);
studentList.add(student_2);
studentList.add(student_3);
// 根据用户传入字段 假设我们要忽略 money
String writePathExclude = "C:/students_write_exclude.xlsx";
Set<String> excludeColumnFiledNames = new HashSet<String>();
excludeColumnFiledNames.add("money");
EasyExcel.write(writePathExclude,Student.class).excludeColumnFiledNames(excludeColumnFiledNames).sheet().doWrite(studentList);
// 根据用户传入字段 假设我们只要 name address
String writePathInclude = "C:/students_write_include.xlsx";
Set<String> includeColumnFiledNames = new HashSet<String>();
includeColumnFiledNames.add("stu_name");
includeColumnFiledNames.add("address");
EasyExcel.write(writePathInclude,Student.class).includeColumnFiledNames(includeColumnFiledNames).sheet().doWrite(studentList);
}
3. 指定写入列
参考官方文档。
-
Excel
字符串标题 日期标题 数字标题 字符串0 2020/4/21 10:13 0.43 字符串1 2020/4/22 10:13 0.43 字符串2 2020/4/23 10:13 0.43 字符串3 2020/4/24 10:13 0.43 字符串4 2020/4/25 10:13 0.43 字符串5 2020/4/26 10:13 0.43 字符串6 2020/4/27 10:13 0.43 字符串7 2020/4/28 10:13 0.43 -
对象
@Data public class IndexData { @ExcelProperty(value = "字符串标题", index = 0) private String string; @ExcelProperty(value = "日期标题", index = 1) private Date date; /** * 这里设置3 会导致第二列空的 */ @ExcelProperty(value = "数字标题", index = 3) private Double doubleData; } -
代码
/** * 指定写入的列 * <p>1. 创建excel对应的实体对象 参照{@link IndexData} * <p>2. 使用{@link ExcelProperty}注解指定写入的列 * <p>3. 直接写即可 */ @Test public void indexWrite() { String fileName = TestFileUtil.getPath() + "indexWrite" + System.currentTimeMillis() + ".xlsx"; // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 EasyExcel.write(fileName, IndexData.class).sheet("模板").doWrite(data()); }
4. 重复多次写入(写到单个或者多个Sheet)
/**
* 重复多次写入(写到单个或者多个Sheet)
* <p>
* 1. 创建excel对应的实体对象 参照{@link ComplexHeadData}
* <p>
* 2. 使用{@link ExcelProperty}注解指定复杂的头
* <p>
* 3. 直接调用二次写入即可
*/
@Test
public void repeatedWrite() {
// 方法1 如果写到同一个sheet
String fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx";
// 这里 需要指定写用哪个class去写
ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build();
// 这里注意 如果同一个sheet只要创建一次
WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
// 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来
for (int i = 0; i < 5; i++) {
// 分页去数据库查询数据 这里可以去数据库查询每一页的数据
List<DemoData> data = data();
excelWriter.write(data, writeSheet);
}
// 千万别忘记finish 会帮忙关闭流
excelWriter.finish();
// 方法2 如果写到不同的sheet 同一个对象
fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx";
// 这里 指定文件
excelWriter = EasyExcel.write(fileName, DemoData.class).build();
// 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来。这里最终会写到5个sheet里面
for (int i = 0; i < 5; i++) {
// 每次都要创建writeSheet 这里注意必须指定sheetNo
writeSheet = EasyExcel.writerSheet(i, "模板").build();
// 分页去数据库查询数据 这里可以去数据库查询每一页的数据
List<DemoData> data = data();
excelWriter.write(data, writeSheet);
}
// 千万别忘记finish 会帮忙关闭流
excelWriter.finish();
// 方法3 如果写到不同的sheet 不同的对象
fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx";
// 这里 指定文件
excelWriter = EasyExcel.write(fileName).build();
// 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来。这里最终会写到5个sheet里面
for (int i = 0; i < 5; i++) {
// 每次都要创建writeSheet 这里注意必须指定sheetNo。这里注意DemoData.class 可以每次都变,我这里为了方便 所以用的同一个class 实际上可以一直变
writeSheet = EasyExcel.writerSheet(i, "模板").head(DemoData.class).build();
// 分页去数据库查询数据 这里可以去数据库查询每一页的数据
List<DemoData> data = data();
excelWriter.write(data, writeSheet);
}
// 千万别忘记finish 会帮忙关闭流
excelWriter.finish();
}
5. 不创建对象写
/**
* 不创建对象的写
*/
@Test
public void noModleWrite() {
// 写法1
String fileName = TestFileUtil.getPath() + "noModleWrite" + System.currentTimeMillis() + ".xlsx";
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
EasyExcel.write(fileName).head(head()).sheet("模板").doWrite(dataList());
}
private List<List<String>> head() {
List<List<String>> list = new ArrayList<List<String>>();
List<String> head0 = new ArrayList<String>();
head0.add("字符串" + System.currentTimeMillis());
List<String> head1 = new ArrayList<String>();
head1.add("数字" + System.currentTimeMillis());
List<String> head2 = new ArrayList<String>();
head2.add("日期" + System.currentTimeMillis());
list.add(head0);
list.add(head1);
list.add(head2);
return list;
}
private List<List<Object>> dataList() {
List<List<Object>> list = new ArrayList<List<Object>>();
for (int i = 0; i < 10; i++) {
List<Object> data = new ArrayList<Object>();
data.add("字符串" + i);
data.add(new Date());
data.add(0.56);
list.add(data);
}
return list;
}
6. 其他复杂的写操作
日期、数字的格式化,列宽、高,自定义样式、单元格合并,web中的写等复杂操作参考官方文档。
五、填充Excel
1. 最简单的填充
-
模板
姓名 数字 复杂 忽略 {name}今年{number}岁 \{name}\忽略, -
最终结果
姓名 数字 复杂 忽略 张三 5.4 张三今年5.4岁 {name}忽略,张三 -
对象
@Data public class FillData { private String name; private double number; } -
代码
/** * 最简单的填充 * * @since 2.1.1 */ @Test public void simpleFill() { // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 String templateFileName = TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "simple.xlsx"; // 方案1 根据对象填充 String fileName = TestFileUtil.getPath() + "simpleFill" + System.currentTimeMillis() + ".xlsx"; // 这里 会填充到第一个sheet, 然后文件流会自动关闭 FillData fillData = new FillData(); fillData.setName("张三"); fillData.setNumber(5.2); EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(fillData); // 方案2 根据Map填充 fileName = TestFileUtil.getPath() + "simpleFill" + System.currentTimeMillis() + ".xlsx"; // 这里 会填充到第一个sheet, 然后文件流会自动关闭 Map<String, Object> map = new HashMap<String, Object>(); map.put("name", "张三"); map.put("number", 5.2); EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(map); }
2. 填充列表
-
模板
姓名 数字 -
最终效果
姓名 数字 张三 5.6 李四 7.9 王五 8.8 赵六 1.9 -
代码
/** * 填充列表 * * @since 2.1.1 */ @Test public void listFill() { // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 // 填充list 的时候还要注意 模板中{.} 多了个点 表示list String templateFileName = TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "list.xlsx"; // 方案1 一下子全部放到内存里面 并填充 String fileName = TestFileUtil.getPath() + "listFill" + System.currentTimeMillis() + ".xlsx"; // 这里 会填充到第一个sheet, 然后文件流会自动关闭 EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(data()); // 方案2 分多次 填充 会使用文件缓存(省内存) fileName = TestFileUtil.getPath() + "listFill" + System.currentTimeMillis() + ".xlsx"; ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); WriteSheet writeSheet = EasyExcel.writerSheet().build(); excelWriter.fill(data(), writeSheet); excelWriter.fill(data(), writeSheet); // 千万别忘记关闭流 excelWriter.finish(); }
3. 复杂填充
复杂填充、大数据量填充、横向填充参考官方文档。
六、常见API
参考官方文档。

浙公网安备 33010602011771号