赞
踩
官网:https://easyexcel.opensource.alibaba.com/
EasyExcel是一个基于Java的、快速、简洁、解决大文件内存溢出的Excel处理工具。
他能让你在不用考虑性能、内存的等因素的情况下,快速完成Excel的读、写等功能。
下面我们直接开始体验Easy Excel的用法
首先我们需要构造一个Excel对象的模型类。其中每个属性代表一列,使用@ExcelProperty
给表格设置表头。
@Getter
@Setter
@AllArgsConstructor
public class DemoData {
@ExcelProperty("字符串标题")
private String string;
@ExcelProperty("日期标题")
private Date date;
@ExcelProperty("数字标题")
private Double doubleData;
}
然后我们就可以写一个测试方法进行测试了,具体代码如下:
请记住次方法,下文中我们会多以使用此方法进行写入Excel,记为
simpleWrite()
,后续将不重复赘述。
@Test public void simpleWrite() { // 1. 构造数据 List<DemoData> demoDataList = Stream.of( new DemoData("字符串1", new Date(), 1.1), new DemoData("字符串2", new Date(), 2.2), new DemoData("字符串3", new Date(), 3.3) ).collect(Collectors.toList()); // 2. 写入Excel表格:方式一 EasyExcel.write("一个测试表格_01.xlsx", DemoData.class) .sheet("第一个sheet") .doWrite(demoDataList); // 3. 写入Excel表格:方式二 ExcelWriter excelWriter = EasyExcel.write("一个测试表格_02.xlsx", DemoData.class).build(); WriteSheet sheet = EasyExcel.writerSheet("第一个sheet").build(); excelWriter.write(demoDataList, sheet); excelWriter.finish(); }
运行结果如下:
在某些场景,我们可能只需要导出部分数据,例如DemoData
中的数据,我们可能只想导出字符串和数字,时间字段不展示给用户看。
这个时候我们有方式可以实现:
@Test public void excludeOrIncludeWrite() { // 1. 构造数据 List<DemoData> demoDataList = Stream.of( new DemoData("字符串1", new Date(), 1.1), new DemoData("字符串2", new Date(), 2.2), new DemoData("字符串3", new Date(), 3.3) ).collect(Collectors.toList()); // 2. 方式一:导出指定项(includeColumnFieldNames) Set<String> includeSet = Stream.of("string", "doubleData").collect(Collectors.toSet()); EasyExcel.write("一个测试表格_01.xlsx", DemoData.class) .includeColumnFieldNames(includeSet) .sheet("第一个sheet") .doWrite(demoDataList); // 3. 方式二:排除指定项(excludeColumnFieldNames) Set<String> excludeSet = Stream.of("date").collect(Collectors.toSet()); EasyExcel.write("一个测试表格_02.xlsx", DemoData.class) .excludeColumnFieldNames(excludeSet) .sheet("第一个sheet") .doWrite(demoDataList); }
运行结果:
@ExcelIgnore
:默认所有字段都会和excel去匹配,加了这个注解会忽略该字段
想实现上述效果,我们值需要将date
字段添加@ExcelIgnore
注解即可。
public class DemoData {
@ExcelProperty(value = {"字符串标题"})
private String string;
@ExcelProperty(value = {"日期标题"})
@ExcelIgnore
private Date date;
@ExcelProperty(value = {"数字标题"})
private Double doubleData;
}
然后调用simpleWrite()
方法写入表格,让我们看看运行结果:
@ExcelIgnoreUnannotated
:默认不管加不加ExcelProperty的注解的所有字段都会参与读写,加了ExcelIgnoreUnannotated注解以后,不加ExcelProperty注解的字段就不会参与。
首先我们先学习下
@ExcelProperty
注解
在上面的例子中,你会发现我们给所有的属性都添加上了@ExcelProperty
注解用来设置表头,那么你可能会疑惑,这个注解是否是与Excel交互必须的注解呢?这个注解除了设置表头还有没有其他功能呢?
我们先来简单看一下这个注解的属性,如下图,其他属性我们先不做过多讲解,后续会一一讲到。
value
属性:用于设置表头,如果没添加@ExcelProperty
注解的字段,就会使用字段名作为默认表头;也就是说,写Excel时默认类中所有的字段都要与Excel中的列进行交互。
了解了@ExcelProperty
之后你应该对@ExcelIgnoreUnannotated
注解有了一定的认识,默认类中所有的属性都会和Excel交互;添加玩@ExcelIgnoreUnannotated
注解后,只有添加了@ExcelProperty
注解的属性才会被写入Excel中。
下面让我们写代码测试一下(依旧使用simpleWriter()
方法进行写入测试):
@Getter
@Setter
@AllArgsConstructor
@ExcelIgnoreUnannotated
public class DemoData {
@ExcelProperty(value = {"字符串标题"})
private String string;
private Date date;
@ExcelProperty(value = {"数字标题"})
private Double doubleData;
}
想要实现吧某个数据插入在指定的列上,我们需要使用到@ExcelProperty
注解,这个注解我们在定义DemoData
中已经使用过,当时我们使用的是它的默认属性:value
,用于设置表头。这里我们就需要使用到它的第二个属性:index
,这个属性代表的是我们这个数据插入的列。
举个栗子:
@Getter
@Setter
@AllArgsConstructor
public class DemoData {
@ExcelProperty(value = "字符串标题", index = 0)
private String string;
@ExcelProperty(value = "日期标题", index = 1)
private Date date;
@ExcelProperty(value = "数字标题", index = 3)
private Double doubleData;
}
在上面得代码中我们给属性设置了插入的列,分别为0、1、3,下面让我们来运行一下上面第一段代码中的simpleWrite()
,看看得到的结果如何呢?
从图中我们可以看到,数字标题被插入到了第3列,而第二列由于没有值,所以为空。
在最开始的例子中我们已经知道如何设置表头。
再回顾一下,使用
@ExcelProperty
注解的value属性即可设置对应列的表头。
但是我们表格的表头并不止这样简单,有时候可能会存在多级表头,如下图所示:
那么这样的功能我们应该如何实现呢?我们先点击@ExcelProperty
注解查看源码:
简单翻译一下:
写:当你有超过一个表头时会自动合并
而且我们仔细观察会发现,value
的类型并不是String,而是String[],那么以为这我们可以在一个注解中设置多个表头,那么设置多个表头能达到什么样的效果呢?我们试验一下便知,如下代码我们给每个属性都设置了两个不同的标题,而且string
和date
字段都是相同的"主题1",按照注释中的说法,他们应该会合并为同一个单元格。下面我们还是使用simpleWrite()
来测试一下
@Getter
@Setter
@AllArgsConstructor
public class DemoData {
@ExcelProperty(value = {"主题1", "字符串标题"}, index = 0)
private String string;
@ExcelProperty(value = {"主题1", "日期标题"}, index = 1)
private Date date;
@ExcelProperty(value = {"主题2", "数字标题"}, index = 3)
private Double doubleData;
}
运行结果:
有上结果我们可以得出:我们需要几级表头,这在注解中直接设置即可,且连续且相同的表头会默认合并。
有时候我们可能需要从数据库中进行分页查询,多次获取数据,重复写入Excel,此时应该怎么实现呢。
其实很简单,不知道你是否还记得简单写入的第二种方式,我们只需要在调用excelWriter.write()
的地方加入一个循环即可实现。
/* 首先我们先构造一个获取数据的方法,模拟分页查询数据库 */ private List<DemoData> getData() { return Stream.of( new DemoData("字符串1", new Date(), 1.1), new DemoData("字符串2", new Date(), 2.2), new DemoData("字符串3", new Date(), 3.3) ).collect(Collectors.toList()); } @Test public void repeatWriter() { ExcelWriter excelWriter = EasyExcel.write("一个测试表格_01.xlsx", DemoData.class).build(); WriteSheet sheet = EasyExcel.writerSheet("第一个sheet").build(); for (int i = 0; i < 5; i++) { List<DemoData> demoDataList = getData(); excelWriter.write(demoDataList, sheet); } excelWriter.finish(); }
那么,如果我们要写入到不同的sheet页中呢?其实也一样,只需要在调用excelWriter.write()
之前,重新生成一个WriteSheet
即可。
/* 首先我们先构造一个获取数据的方法,模拟分页查询数据库 */ private List<DemoData> getData() { return Stream.of( new DemoData("字符串1", new Date(), 1.1), new DemoData("字符串2", new Date(), 2.2), new DemoData("字符串3", new Date(), 3.3) ).collect(Collectors.toList()); } @Test public void repeatWriter() { ExcelWriter excelWriter = EasyExcel.write("一个测试表格_01.xlsx", DemoData.class).build(); for (int i = 0; i < 5; i++) { List<DemoData> demoDataList = getData(); WriteSheet sheet = EasyExcel.writerSheet("第" + i + "个sheet").build(); excelWriter.write(demoDataList, sheet); } excelWriter.finish(); }
格式转换在我们导出Excel时是十分常用的功能。有时候我们对Excel中展示的格式有特殊的需求,且和数据库中不一致,这怎么办呢?
修改数据库中的存储类型,全量刷新表?这不用解释,当然是不可接受的。
在每次写入Excel之前先手动转换一把格式?功能倒是可以实现,但是太复杂了。
其实EasyExcel
为我们提供了多种转换格式的方式,让我们一起来看看吧
首先我们先来看一个功能最强的格式转换,还记得@ExcelProperty
注解有一个converter
属性吗,没错,这就是用来设置转换器的属性。
对于写的场景来说,我们只需要实现下面得方法即可:
com.alibaba.excel.converters.Converter#convertToExcelData(com.alibaba.excel.converters.WriteConverterContext<T>)
例如我们想把string
字段的格式统一为 “字符串转换:XXX”,让我们看看转换器的代码:
public class StringConverter implements Converter<String> {
@Override
public WriteCellData<String> convertToExcelData(String value, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) throws Exception {
String cellData = "字符串转换:" + value;
return new WriteCellData<>(cellData);
}
}
转换器有了,我们怎么让转换器生效呢?EasyExcel为我们提供了3中方式
3中载入方式我们这里不做过多赘述,使用方法和原理可以参考下面这篇博客:
https://blog.csdn.net/fsadkjl/article/details/105823830
这里我们就是用@ExcelProperty
注解的方式来演示,首先在string
字段的@ExcelProperty
加上converter =自定义转换器类
,然后依旧还是使用simpleWriter()
进行测试。
public class DemoData {
@ExcelProperty(value = {"字符串标题"}, converter = StringConverter.class)
private String string;
@ExcelProperty(value = {"日期标题"})
private Date date;
@ExcelProperty(value = {"数字标题"})
private Double doubleData;
}
想要转换日期格式,使用自定义格式转换也可以实现,但是EasyExcel为我们提供了一种更简单的方式:@DateTimeFormat
使用方式也很简单,只需要在想要转换的Date字段上添加该注解即可,下面来看一看代码和运行结果
public class DemoData {
@ExcelProperty(value = {"字符串标题"}, converter = StringConverter.class)
private String string;
@DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")
@ExcelProperty(value = {"日期标题"})
private Date date;
@ExcelProperty(value = {"数字标题"})
private Double doubleData;
}
同样的EasyExcel也为我们提供了更简单的数字转换器:@NumberFormat("#.##%")
@ExcelIgnoreUnannotated
public class DemoData {
@ExcelProperty(value = {"字符串标题"}, converter = StringConverter.class)
private String string;
@DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")
@ExcelProperty(value = {"日期标题"})
private Date date;
@NumberFormat("#.##%") // 转换为百分比形式
@ExcelProperty(value = {"数字标题"})
private Double doubleData;
}
下面的例子用5种方式导入图片,我们直接看代码
实体类:
@Data
public class ImageDemoData {
private File file;
private InputStream inputStream;
@ExcelProperty(converter = StringImageConverter.class)
private String string;
private byte[] byteArray;
private URL url;
}
测试方法:
@Test public void imageSimpleWrite() throws IOException { ImageDemoData imageDemoData = new ImageDemoData(); // 方式一:File imageDemoData.setFile(new File("test.jpg")); // 方式二:InputStream imageDemoData.setInputStream(new FileInputStream("test.jpg")); // 方式三:String imageDemoData.setString("test.jpg"); // 方式四:byte[] byte[] b = new byte[(int) new File("test.jpg").length()]; FileInputStream in = new FileInputStream("test.jpg"); in.read(b, 0, (int) new File("test.jpg").length()); imageDemoData.setByteArray(b); // 方式五:url imageDemoData.setUrl(new URL("https://img1.baidu.com/it/u=3539595421,754041626&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1691168400&t=92147dc54c087237b747be417bfdc579")); // 2. 写Excel表格测试 EasyExcel.write("一个测试表格_03.xlsx", ImageDemoData.class) .sheet("第一个sheet") .doWrite(Collections.singletonList(imageDemoData)); }
执行结果:
@Data public class WriteCellDemoData { /** * 超链接 * * @since 3.0.0-beta1 */ private WriteCellData<String> hyperlink; } public class EasyExcelTest { @Test public void writeCellDataWrite() { WriteCellDemoData writeCellDemoData = new WriteCellDemoData(); // 设置超链接 WriteCellData<String> hyperlink = new WriteCellData<>("百度网站"); writeCellDemoData.setHyperlink(hyperlink); HyperlinkData hyperlinkData = new HyperlinkData(); hyperlink.setHyperlinkData(hyperlinkData); hyperlinkData.setAddress("https://www.baidu.com"); hyperlinkData.setHyperlinkType(HyperlinkData.HyperlinkType.URL); EasyExcel.write("一个测试表格_01.xlsx", WriteCellDemoData.class) .inMemory(true) .sheet("第一个sheet") .doWrite(Collections.singletonList(writeCellDemoData)); } }
@Data public class WriteCellDemoData { /** * 备注 * * @since 3.0.0-beta1 */ private WriteCellData<String> commentData; } public class EasyExcelTest { @Test public void writeCellDataWrite() { WriteCellDemoData writeCellDemoData = new WriteCellDemoData(); // 设置备注 WriteCellData<String> comment = new WriteCellData<>("备注的单元格信息"); CommentData commentData = new CommentData(); comment.setCommentData(commentData); commentData.setAuthor("Jiaju Zhuang"); commentData.setRichTextStringData(new RichTextStringData("这是一个备注")); // 备注的默认大小是按照单元格的大小 这里想调整到4个单元格那么大 所以向后 向下 各额外占用了一个单元格 commentData.setRelativeLastColumnIndex(1); commentData.setRelativeLastRowIndex(1); writeCellDemoData.setCommentData(comment); EasyExcel.write("一个测试表格_01.xlsx", WriteCellDemoData.class) .inMemory(true) .sheet("第一个sheet") .doWrite(Collections.singletonList(writeCellDemoData)); } }
执行结果:
@Data public class WriteCellDemoData { /** * 公式 * * @since 3.0.0-beta1 */ private WriteCellData<String> formulaData; } public class EasyExcelTest { @Test public void writeCellDataWrite() { WriteCellDemoData writeCellDemoData = new WriteCellDemoData(); // 设置公式 WriteCellData<String> formula = new WriteCellData<>(); writeCellDemoData.setFormulaData(formula); FormulaData formulaData = new FormulaData(); formula.setFormulaData(formulaData); // 将 123456789 中的第一个数字替换成 2 // 这里只是例子 如果真的涉及到公式 能内存算好尽量内存算好 公式能不用尽量不用 formulaData.setFormulaValue("REPLACE(123456789,1,1,2)"); EasyExcel.write("一个测试表格_01.xlsx", WriteCellDemoData.class) .inMemory(true) .sheet("第一个sheet") .doWrite(Collections.singletonList(writeCellDemoData)); } }
@Data public class WriteCellDemoData { /** * 指定单元格的样式。当然样式 也可以用注解等方式。 * * @since 3.0.0-beta1 */ private WriteCellData<String> writeCellStyle; } public class EasyExcelTest { @Test public void writeCellDataWrite() { WriteCellDemoData writeCellDemoData = new WriteCellDemoData(); // 设置单个单元格的样式 当然样式 很多的话 也可以用注解等方式。 WriteCellData<String> writeCellStyle = new WriteCellData<>("单元格样式"); writeCellStyle.setType(CellDataTypeEnum.STRING); writeCellDemoData.setWriteCellStyle(writeCellStyle); WriteCellStyle writeCellStyleData = new WriteCellStyle(); writeCellStyle.setWriteCellStyle(writeCellStyleData); // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色. writeCellStyleData.setFillPatternType(FillPatternType.SOLID_FOREGROUND); // 背景绿色 writeCellStyleData.setFillForegroundColor(IndexedColors.GREEN.getIndex()); EasyExcel.write("一个测试表格_01.xlsx", WriteCellDemoData.class) .inMemory(true) .sheet("第一个sheet") .doWrite(Collections.singletonList(writeCellDemoData)); } }
@Data public class WriteCellDemoData { /** * 指定一个单元格有多个样式 * * @since 3.0.0-beta1 */ private WriteCellData<String> richText; } public class EasyExcelTest { @Test public void writeCellDataWrite() { WriteCellDemoData writeCellDemoData = new WriteCellDemoData(); // 设置单个单元格多种样式 // 这里需要设置 inMomery=true 不然会导致无法展示单个单元格多种样式,所以慎用 WriteCellData<String> richTest = new WriteCellData<>(); richTest.setType(CellDataTypeEnum.RICH_TEXT_STRING); writeCellDemoData.setRichText(richTest); RichTextStringData richTextStringData = new RichTextStringData(); richTest.setRichTextStringDataValue(richTextStringData); richTextStringData.setTextString("红色绿色默认"); // 前2个字红色 WriteFont writeFont = new WriteFont(); writeFont.setColor(IndexedColors.RED.getIndex()); richTextStringData.applyFont(0, 2, writeFont); // 接下来2个字绿色 writeFont = new WriteFont(); writeFont.setColor(IndexedColors.GREEN.getIndex()); richTextStringData.applyFont(2, 4, writeFont); EasyExcel.write("一个测试表格_01.xlsx", WriteCellDemoData.class) .inMemory(true) .sheet("第一个sheet") .doWrite(Collections.singletonList(writeCellDemoData)); } }
@Test
public void templateWrite() {
// 1. 构造数据
List<DemoData> demoDataList = Stream.of(
new DemoData("字符串1", new Date(), 1.1),
new DemoData("字符串2", new Date(), 2.2),
new DemoData("字符串3", new Date(), 3.3)
).collect(Collectors.toList());
// 2. 写模板
EasyExcel.write("一个测试表格_01.xlsx", DemoData.class)
.withTemplate("一个测试表格_02.xlsx")
.sheet()
.doWrite(demoDataList);
}
设置行宽、列高有3个注解:
顾名思义,他们分别用来设置表头行高、内容行高、列宽。
其中@ColumnWidth
可以放在类上也可以放在字段上,放在类上即设置默认的列宽,放在字段上,即为这个字段的对应列设置列宽。
@Getter @Setter @AllArgsConstructor @ExcelIgnoreUnannotated @ContentRowHeight(10) @HeadRowHeight(20) @ColumnWidth(25) public class DemoData { @ExcelProperty(value = {"字符串标题"}, converter = StringConverter.class) private String string; @DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒") @ExcelProperty(value = {"日期标题"}) private Date date; @NumberFormat("#.##%") // 转换为百分比形式 @ExcelProperty(value = {"数字标题"}) @ColumnWidth(50) private Double doubleData; }
此处我们设置表头高度为20、内容高度为10、列默认长度为25、"数字标题"列的长度为50;依旧使用simpleWrite
写入,结果如下:
@Getter @Setter @AllArgsConstructor @EqualsAndHashCode // 头背景设置成红色 @HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 10) // 头字体设置成20 @HeadFontStyle(fontHeightInPoints = 20) // 内容的背景设置成绿色 @ContentStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 17) // 内容字体设置成20 @ContentFontStyle(fontHeightInPoints = 20) public class DemoStyleData { // 字符串的头背景设置成粉红 @HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 14) // 字符串的头字体设置成20 @HeadFontStyle(fontHeightInPoints = 30) // 字符串的内容的背景设置成天蓝 @ContentStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 40) // 字符串的内容字体设置成20 @ContentFontStyle(fontHeightInPoints = 30) @ExcelProperty("字符串标题") private String string; @ExcelProperty("日期标题") private Date date; @ExcelProperty("数字标题") private Double doubleData; } @Test public void templateWrite() { // 1. 构造数据 List<DemoData> demoDataList = Stream.of( new DemoData("字符串1", new Date(), 1.1), new DemoData("字符串2", new Date(), 2.2), new DemoData("字符串3", new Date(), 3.3) ).collect(Collectors.toList()); // 2. 写模板 EasyExcel.write("一个测试表格_01.xlsx", DemoData.class) .withTemplate("一个测试表格_02.xlsx") .sheet() .doWrite(demoDataList); }
@Test public void handlerStyleWrite() { // 1. 构造数据 List<DemoStyleData> demoDataList = Stream.of( new DemoStyleData("字符串1", new Date(), 1.1), new DemoStyleData("字符串2", new Date(), 2.2), new DemoStyleData("字符串3", new Date(), 3.3) ).collect(Collectors.toList()); // HorizontalCellStyleStrategy 每一行的样式都一样 或者隔行一样 // AbstractVerticalCellStyleStrategy 每一列的样式都一样 需要自己回调每一页 // 头的策略 WriteCellStyle headWriteCellStyle = new WriteCellStyle(); // 背景设置为红色 headWriteCellStyle.setFillForegroundColor(IndexedColors.RED.getIndex()); WriteFont headWriteFont = new WriteFont(); headWriteFont.setFontHeightInPoints((short) 20); headWriteCellStyle.setWriteFont(headWriteFont); // 内容的策略 WriteCellStyle contentWriteCellStyle = new WriteCellStyle(); // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定 contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND); // 背景绿色 contentWriteCellStyle.setFillForegroundColor(IndexedColors.GREEN.getIndex()); WriteFont contentWriteFont = new WriteFont(); // 字体大小 contentWriteFont.setFontHeightInPoints((short) 20); contentWriteCellStyle.setWriteFont(contentWriteFont); // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现 HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle); // 2. 写入Excel表格:方式一 EasyExcel.write("一个测试表格_01.xlsx", DemoData.class) .registerWriteHandler(horizontalCellStyleStrategy) .sheet("第一个sheet") .doWrite(demoDataList); }
@ContentLoopMerge(eachRow = 2)
每两行合并为一行
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。