Excel文件导出

2021-08-30 16:49:48 11090 0 0 0

常用读方式:

读为ExcelReader对象

读取Excel内容的封装,通过构造ExcelReader对象,ExcelUtil.getReader方法只是将实体Excel文件转换为ExcelReader对象进行操作。

  1. 从文件中读取Excel为ExcelReader

java ExcelReader reader = ExcelUtil.getReader(FileUtil.file("test.xlsx"));//默认读取第一个sheet

  1. 从流中读取Excel为ExcelReader(比如从ClassPath中读取Excel文件)

java ExcelReader reader = ExcelUtil.getReader(ResourceUtil.getStream("aaa.xlsx"));//默认读取第一个sheet

  1. 读取指定的sheet

java //通过sheet编号获取 sheet编号从0开始 ExcelReader reader = ExcelUtil.getReader(FileUtil.file("test.xlsx"), 0); //通过sheet名获取 ExcelReader reader = ExcelUtil.getReader(FileUtil.file("test.xlsx"), "sheet1");

处理ExcelReader对象

指定被读取的Excel文件、流或工作簿,然后调用readXXX方法读取内容为指定格式。

ExcelReader reader = ExcelUtil.getReader("d:/aaa.xlsx");
  1. 读取Excel中所有行和列,都用列表表示(包括标题行也会被读出)

java List<List<Object>> readAll = reader.read();

  1. 读取为Map列表,默认第一行为标题行,Map中的key为标题,value为标题对应的单元格值。

java List<Map<String,Object>> readAll = reader.readAll();

  1. 读取为Bean列表,Bean中的字段名为标题,字段值为标题对应的单元格值。

java //标题字段和对象属性名不一致时,通过 reader.addHeaderAlias 方法设置别名 reader.addHeaderAlias("班级","grade"); List<Person> all = reader.readAll(Person.class);

大数量读取方式

在标准的ExcelReader中,如果数据量较大,读取Excel会非常缓慢,并有可能造成内存溢出。因此针对大数据量的Excel,Hutool封装了Sax模式的读取方式。

首先我们实现一下RowHandler接口,这个接口是Sax读取的核心,通过实现handle方法编写我们要对每行数据的操作方式(比如按照行入库,入List或者写出到文件等),在此我们只是在控制台打印。

private RowHandler createRowHandler() {
    return new RowHandler() {
        @Override
        public void handle(int sheetIndex, long rowIndex, List<Object> rowlist) {
            Console.log("[{}] [{}] {}", sheetIndex, rowIndex, rowlist);
        }
    };
}

ExcelUtil.readBySax("aaa.xlsx", 0, createRowHandler());

Excel导出

原理

Hutool将Excel写出封装为ExcelWriter,原理为包装了Workbook对象,每次调用merge(合并单元格)或者write(写出数据)方法后只是将数据写入到Workbook,并不写出文件,只有调用flush或者close方法后才会真正写出文件。

由于机制原因,在写出结束后需要关闭ExcelWriter对象,调用close方法即可关闭,此时才会释放Workbook对象资源,否则带有数据的Workbook一直会常驻内存。

常用写出方法

1.将行列对象写出到Excel

先定义一个嵌套的List,List的元素也是一个List,内层的一个List代表一行数据,每行都有4个单元格,最终list对象代表多行数据。

List<String> row1 = CollUtil.newArrayList("aa", "bb", "cc", "dd");
List<String> row2 = CollUtil.newArrayList("aa1", "bb1", "cc1", "dd1");
List<String> row3 = CollUtil.newArrayList("aa2", "bb2", "cc2", "dd2");
List<String> row4 = CollUtil.newArrayList("aa3", "bb3", "cc3", "dd3");
List<String> row5 = CollUtil.newArrayList("aa4", "bb4", "cc4", "dd4");

List<List<String>> rows = CollUtil.newArrayList(row1, row2, row3, row4, row5);

然后我们使用创建的ExcelWriter对象后写出数据:

//通过工具类创建writer
ExcelWriter writer = ExcelUtil.getWriter("d:/writeTest.xlsx");
//通过构造方法创建writer
//ExcelWriter writer = new ExcelWriter("d:/writeTest.xls");

//跳过当前行,既第一行,非必须,在此演示用
writer.passCurrentRow();

//合并单元格后的标题行,使用默认标题样式
writer.merge(row1.size() - 1, "测试标题");
//一次性写出内容,强制输出标题
writer.write(rows, true);
//关闭writer,释放内存
writer.close();

效果图1

2.写出Map数据
Map<String, Object> row1 = new LinkedHashMap<>();
row1.put("姓名", "张三");
row1.put("年龄", 23);
row1.put("成绩", 88.32);
row1.put("是否合格", true);
row1.put("考试日期", DateUtil.date());

Map<String, Object> row2 = new LinkedHashMap<>();
row2.put("姓名", "李四");
row2.put("年龄", 33);
row2.put("成绩", 59.50);
row2.put("是否合格", false);
row2.put("考试日期", DateUtil.date());

ArrayList<Map<String, Object>> rows = CollUtil.newArrayList(row1, row2);
// 通过工具类创建writer
ExcelWriter writer = ExcelUtil.getWriter("d:/writeMapTest.xlsx");
// 合并单元格后的标题行,使用默认标题样式
writer.merge(row1.size() - 1, "一班成绩单");
// 一次性写出内容,使用默认样式,强制输出标题
writer.write(rows, true);
// 关闭writer,释放内存
writer.close();

效果图2

3.写出Bean数据
//创建一个实体类
public class TestBean {
    private String name;
    private int age;
    private double score;
    private boolean isPass;
    private Date examDate;
}
TestBean bean1 = new TestBean("张三",22,66.30,true,DateUtil.date());
TestBean bean2 = new TestBean("李四",28,38.50,false,DateUtil.date());
List<TestBean> rows = CollUtil.newArrayList(bean1, bean2);//添加到集合
// 通过工具类创建writer
ExcelWriter writer = ExcelUtil.getWriter("d:/writeBeanTest.xlsx");
// 合并单元格后的标题行,使用默认标题样式
writer.merge(4, "一班成绩单");
// 一次性写出内容,使用默认样式,强制输出标题
writer.write(rows, true);
// 关闭writer,释放内存
writer.close();

效果图3

4.自定义Bean的key别名(排序标题)

调用ExcelWriter对象的addHeaderAlias方法自定义Bean中key的别名,这样就可以写出自定义标题了

默认情况下Excel中写出Bean字段不能保证顺序,此时可以使用addHeaderAlias方法设置标题别名,Bean的写出顺序就会按照标题别名的加入顺序排序。

如果不需要设置标题但是想要排序字段,请调用writer.addHeaderAlias("age", "age")设置一个相同的别名就可以不更换标题。 未设置标题别名的字段不参与排序,会默认排在前面。

// 通过工具类创建writer
ExcelWriter writer = ExcelUtil.getWriter("d:/writeBeanTest.xlsx");

//自定义标题别名
writer.addHeaderAlias("name", "姓名");
writer.addHeaderAlias("age", "年龄");
writer.addHeaderAlias("score", "分数");
writer.addHeaderAlias("isPass", "是否通过");
writer.addHeaderAlias("examDate", "考试时间");

// 合并单元格后的标题行,使用默认标题样式
writer.merge(4, "一班成绩单");
// 一次性写出内容,使用默认样式,强制输出标题
writer.write(rows, true);
// 关闭writer,释放内存
writer.close();

效果图4

自定义Excel

1.设置单元格背景色
ExcelWriter writer = ...;

// 定义单元格背景色
StyleSet style = writer.getStyleSet();
// 第二个参数表示是否也设置头部单元格背景
style.setBackgroundColor(IndexedColors.RED, false);
2.自定义字体
ExcelWriter writer = ...;
//设置内容字体
Font font = writer.createFont();
font.setBold(true);
font.setColor(Font.COLOR_RED); 
font.setItalic(true); 
//第二个参数表示是否忽略头部样式
writer.getStyleSet().setFont(font, true);
3.写出多个sheet
//初始化时定义表名
ExcelWriter writer = new ExcelWriter("d:/aaa.xls", "表1");
//切换sheet,此时从第0行开始写
writer.setSheet("表2");
...
writer.setSheet("表3");
...
4.更详细的定义样式

在Excel中,由于样式对象个数有限制,因此Hutool根据样式种类分为4个样式对象,使相同类型的单元格可以共享样式对象。样式按照类别存在于StyleSet中,其中包括:

  • 头部样式 headCellStyle
  • 普通单元格样式 cellStyle
  • 数字单元格样式 cellStyleForNumber
  • 日期单元格样式 cellStyleForDate

其中cellStyleForNumber cellStyleForDate用于控制数字和日期的显示方式。

因此我们可以使用以下方式获取CellStyle对象自定义指定种类的样式:

StyleSet style = writer.getStyleSet();
CellStyle cellStyle = style.getHeadCellStyle();
...

大数据量处理

对于大量数据输出,采用ExcelWriter容易引起内存溢出,因此有了BigExcelWriter,使用方法与ExcelWriter完全一致。

BigExcelWriter writer= ExcelUtil.getBigWriter("e:/xxx.xlsx");
// 一次性写出内容,使用默认样式
writer.write(rows);
// 关闭writer,释放内存
writer.close();