EasyExcel的使用

EasyExcel的使用

  • 1 EasyExcel
  • 2 依赖
    • 2.1 maven
    • 2.2 gradle
  • 3 导出(写)Excel
    • 3.1 简单的写法
    • 3.2 指定写入的列
    • 3.3 合并表头
    • 3.4 日期、数字或者自定义格式转换
    • 3.5 图片导出
    • 3.6 超链接、备注、公式、指定单个单元格的样式、单个单元格多种样式
    • 3.7 列宽、行高
    • 3.8 样式自定义
    • 3.8 注解形式自定义样式
    • 3.9 合并单元格
    • 3.10
  • 4 导入(读)Excel
    • 4.1 最简单的读
    • 4.2 日期、数字或者自定义格式转换
  • 5 根据模板填充Excel
  • 6 注解
    • 6.1 @ContentFontStyle参数
    • 6.2 @ContentLoopMerge
    • 6.3 @OnceAbsoluteMerge
    • 6.4 @ContentStyle参数
    • 6.5 @HeadFontStyle 参数
    • 6. @HeadStyle参数
  • 7 Demo

1 EasyExcel

Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。
easyexcel重写了poi对07版Excel的解析,一个3M的excel用POI sax解析依然需要100M左右内存,改用easyexcel可以降低到几M,并且再大的excel也不会出现内存溢出;03版依赖POI的sax模式,在上层做了模型转换的封装,让使用者更加简单方便

看教程请移步
官网:https://easyexcel.opensource.alibaba.com/
这里只记录开发中使用过的

2 依赖

2.1 maven

<dependency>
    <groupId>com.alibabagroupId>
    <artifactId>easyexcelartifactId>
    <version>3.2.0version>
dependency>

2.2 gradle

implementation 'com.alibaba:easyexcel:3.2.0'

3 导出(写)Excel

3.1 简单的写法

  1. 官网对象:
@Getter
@Setter
@EqualsAndHashCode
public class DemoData {
    @ExcelProperty("字符串标题")
    private String string;
    @ExcelProperty("日期标题")
    private Date date;
    @ExcelProperty("数字标题")
    private Double doubleData;
    /**
     * 忽略这个字段
     */
    @ExcelIgnore
    private String ignore;
}
  1. 官网示例:
    /**
     * 最简单的写
     * 

* 1. 创建excel对应的实体对象 参照{@link DemoData} *

* 2. 直接写即可 */ @Test public void simpleWrite() { // 注意 simpleWrite在数据量不大的情况下可以使用(5000以内,具体也要看实际情况),数据量大参照 重复多次写入 // 写法1 JDK8+ // since: 3.0.0-beta1 String fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx"; // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 // 如果这里想使用03 则 传入excelType参数即可 EasyExcel.write(fileName, DemoData.class) .sheet("模板") .doWrite(() -> { // 分页查询数据 return data(); }); // 写法2 fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx"; // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 // 如果这里想使用03 则 传入excelType参数即可 EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data()); // 写法3 fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx"; // 这里 需要指定写用哪个class去写 try (ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build()) { WriteSheet writeSheet = EasyExcel.writerSheet("模板").build(); excelWriter.write(data(), writeSheet); } }

3.2 指定写入的列

  1. 官网对象:
@Getter
@Setter
@EqualsAndHashCode
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;
}
  1. 官网示例:
    /**
     * 指定写入的列
     * 

1. 创建excel对应的实体对象 参照{@link IndexData} *

2. 使用{@link ExcelProperty}注解指定写入的列 *

3. 直接写即可 */ @Test public void indexWrite() { String fileName = TestFileUtil.getPath() + "indexWrite" + System.currentTimeMillis() + ".xlsx"; // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 EasyExcel.write(fileName, IndexData.class).sheet("模板").doWrite(data()); }

3.3 合并表头

  1. 官网对象:
@Getter
@Setter
@EqualsAndHashCode
public class ComplexHeadData {
    @ExcelProperty({"主标题", "字符串标题"})
    private String string;
    @ExcelProperty({"主标题", "日期标题"})
    private Date date;
    @ExcelProperty({"主标题", "数字标题"})
    private Double doubleData;
}
  1. 官网示例:
    /**
     * 复杂头写入
     * 

1. 创建excel对应的实体对象 参照{@link ComplexHeadData} *

2. 使用{@link ExcelProperty}注解指定复杂的头 *

3. 直接写即可 */ @Test public void complexHeadWrite() { String fileName = TestFileUtil.getPath() + "complexHeadWrite" + System.currentTimeMillis() + ".xlsx"; // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 EasyExcel.write(fileName, ComplexHeadData.class).sheet("模板").doWrite(data()); }

3.4 日期、数字或者自定义格式转换

  1. 官网对象:
@Getter
@Setter
@EqualsAndHashCode
public class ConverterData {
    /**
     * 我想所有的 字符串起前面加上"自定义:"三个字
     */
    @ExcelProperty(value = "字符串标题", converter = CustomStringStringConverter.class)
    private String string;
    /**
     * 我想写到excel 用年月日的格式
     */
    @DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")
    @ExcelProperty("日期标题")
    private Date date;
    /**
     * 我想写到excel 用百分比表示
     */
    @NumberFormat("#.##%")
    @ExcelProperty(value = "数字标题")
    private Double doubleData;
}
  1. 官网示例:
    /**
     * 日期、数字或者自定义格式转换
     * 

1. 创建excel对应的实体对象 参照{@link ConverterData} *

2. 使用{@link ExcelProperty}配合使用注解{@link DateTimeFormat}、{@link NumberFormat}或者自定义注解 *

3. 直接写即可 */ @Test public void converterWrite() { String fileName = TestFileUtil.getPath() + "converterWrite" + System.currentTimeMillis() + ".xlsx"; // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 EasyExcel.write(fileName, ConverterData.class).sheet("模板").doWrite(data()); }

3.5 图片导出

  1. 官网对象:
@Getter
@Setter
@EqualsAndHashCode
@ContentRowHeight(100)
@ColumnWidth(100 / 8)
public class ImageDemoData {
    private File file;
    private InputStream inputStream;
    /**
     * 如果string类型 必须指定转换器,string默认转换成string
     */
    @ExcelProperty(converter = StringImageConverter.class)
    private String string;
    private byte[] byteArray;
    /**
     * 根据url导出
     *
     * @since 2.1.1
     */
    private URL url;

    /**
     * 根据文件导出 并设置导出的位置。
     *
     * @since 3.0.0-beta1
     */
    private WriteCellData<Void> writeCellDataFile;
}
  1. 官网示例:
   /**
     * 图片导出
     * 

* 1. 创建excel对应的实体对象 参照{@link ImageDemoData} *

* 2. 直接写即可 */ @Test public void imageWrite() throws Exception { String fileName = TestFileUtil.getPath() + "imageWrite" + System.currentTimeMillis() + ".xlsx"; String imagePath = TestFileUtil.getPath() + "converter" + File.separator + "img.jpg"; try (InputStream inputStream = FileUtils.openInputStream(new File(imagePath))) { List<ImageDemoData> list = ListUtils.newArrayList(); ImageDemoData imageDemoData = new ImageDemoData(); list.add(imageDemoData); // 放入五种类型的图片 实际使用只要选一种即可 imageDemoData.setByteArray(FileUtils.readFileToByteArray(new File(imagePath))); imageDemoData.setFile(new File(imagePath)); imageDemoData.setString(imagePath); imageDemoData.setInputStream(inputStream); imageDemoData.setUrl(new URL( "https://raw.githubusercontent.com/alibaba/easyexcel/master/src/test/resources/converter/img.jpg")); // 这里演示 // 需要额外放入文字 // 而且需要放入2个图片 // 第一个图片靠左 // 第二个靠右 而且要额外的占用他后面的单元格 WriteCellData<Void> writeCellData = new WriteCellData<>(); imageDemoData.setWriteCellDataFile(writeCellData); // 这里可以设置为 EMPTY 则代表不需要其他数据了 writeCellData.setType(CellDataTypeEnum.STRING); writeCellData.setStringValue("额外的放一些文字"); // 可以放入多个图片 List<ImageData> imageDataList = new ArrayList<>(); ImageData imageData = new ImageData(); imageDataList.add(imageData); writeCellData.setImageDataList(imageDataList); // 放入2进制图片 imageData.setImage(FileUtils.readFileToByteArray(new File(imagePath))); // 图片类型 imageData.setImageType(ImageType.PICTURE_TYPE_PNG); // 上 右 下 左 需要留空 // 这个类似于 css 的 margin // 这里实测 不能设置太大 超过单元格原始大小后 打开会提示修复。暂时未找到很好的解法。 imageData.setTop(5); imageData.setRight(40); imageData.setBottom(5); imageData.setLeft(5); // 放入第二个图片 imageData = new ImageData(); imageDataList.add(imageData); writeCellData.setImageDataList(imageDataList); imageData.setImage(FileUtils.readFileToByteArray(new File(imagePath))); imageData.setImageType(ImageType.PICTURE_TYPE_PNG); imageData.setTop(5); imageData.setRight(5); imageData.setBottom(5); imageData.setLeft(50); // 设置图片的位置 假设 现在目标 是 覆盖 当前单元格 和当前单元格右边的单元格 // 起点相对于当前单元格为0 当然可以不写 imageData.setRelativeFirstRowIndex(0); imageData.setRelativeFirstColumnIndex(0); imageData.setRelativeLastRowIndex(0); // 前面3个可以不写 下面这个需要写 也就是 结尾 需要相对当前单元格 往右移动一格 // 也就是说 这个图片会覆盖当前单元格和 后面的那一格 imageData.setRelativeLastColumnIndex(1); // 写入数据 EasyExcel.write(fileName, ImageDemoData.class).sheet().doWrite(list); } }

3.6 超链接、备注、公式、指定单个单元格的样式、单个单元格多种样式

  1. 官网对象:
@Getter
@Setter
@EqualsAndHashCode
public class WriteCellDemoData {
    /**
     * 超链接
     *
     * @since 3.0.0-beta1
     */
    private WriteCellData<String> hyperlink;

    /**
     * 备注
     *
     * @since 3.0.0-beta1
     */
    private WriteCellData<String> commentData;

    /**
     * 公式
     *
     * @since 3.0.0-beta1
     */
    private WriteCellData<String> formulaData;

    /**
     * 指定单元格的样式。当然样式 也可以用注解等方式。
     *
     * @since 3.0.0-beta1
     */
    private WriteCellData<String> writeCellStyle;

    /**
     * 指定一个单元格有多个样式
     *
     * @since 3.0.0-beta1
     */
    private WriteCellData<String> richText;
}
  1. 官网示例:
    /**
     * 超链接、备注、公式、指定单个单元格的样式、单个单元格多种样式
     * 

* 1. 创建excel对应的实体对象 参照{@link WriteCellDemoData} *

* 2. 直接写即可 * * @since 3.0.0-beta1 */ @Test public void writeCellDataWrite() { String fileName = TestFileUtil.getPath() + "writeCellDataWrite" + System.currentTimeMillis() + ".xlsx"; WriteCellDemoData writeCellDemoData = new WriteCellDemoData(); // 设置超链接 WriteCellData<String> hyperlink = new WriteCellData<>("官方网站"); writeCellDemoData.setHyperlink(hyperlink); HyperlinkData hyperlinkData = new HyperlinkData(); hyperlink.setHyperlinkData(hyperlinkData); hyperlinkData.setAddress("https://github.com/alibaba/easyexcel"); hyperlinkData.setHyperlinkType(HyperlinkType.URL); // 设置备注 WriteCellData<String> comment = new WriteCellData<>("备注的单元格信息"); writeCellDemoData.setCommentData(comment); CommentData commentData = new CommentData(); comment.setCommentData(commentData); commentData.setAuthor("Jiaju Zhuang"); commentData.setRichTextStringData(new RichTextStringData("这是一个备注")); // 备注的默认大小是按照单元格的大小 这里想调整到4个单元格那么大 所以向后 向下 各额外占用了一个单元格 commentData.setRelativeLastColumnIndex(1); commentData.setRelativeLastRowIndex(1); // 设置公式 WriteCellData<String> formula = new WriteCellData<>(); writeCellDemoData.setFormulaData(formula); FormulaData formulaData = new FormulaData(); formula.setFormulaData(formulaData); // 将 123456789 中的第一个数字替换成 2 // 这里只是例子 如果真的涉及到公式 能内存算好尽量内存算好 公式能不用尽量不用 formulaData.setFormulaValue("REPLACE(123456789,1,1,2)"); // 设置单个单元格的样式 当然样式 很多的话 也可以用注解等方式。 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()); // 设置单个单元格多种样式 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); List<WriteCellDemoData> data = new ArrayList<>(); data.add(writeCellDemoData); EasyExcel.write(fileName, WriteCellDemoData.class).inMemory(true).sheet("模板").doWrite(data); }

3.7 列宽、行高

  1. 官网对象:
@Getter
@Setter
@EqualsAndHashCode
@ContentRowHeight(10)
@HeadRowHeight(20)
@ColumnWidth(25)
public class WidthAndHeightData {
    @ExcelProperty("字符串标题")
    private String string;
    @ExcelProperty("日期标题")
    private Date date;
    /**
     * 宽度为50
     */
    @ColumnWidth(50)
    @ExcelProperty("数字标题")
    private Double doubleData;
}
  1. 官网示例:
    /**
     * 列宽、行高
     * 

1. 创建excel对应的实体对象 参照{@link WidthAndHeightData} *

2. 使用注解{@link ColumnWidth}、{@link HeadRowHeight}、{@link ContentRowHeight}指定宽度或高度 *

3. 直接写即可 */ @Test public void widthAndHeightWrite() { String fileName = TestFileUtil.getPath() + "widthAndHeightWrite" + System.currentTimeMillis() + ".xlsx"; // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 EasyExcel.write(fileName, WidthAndHeightData.class).sheet("模板").doWrite(data()); }

3.8 样式自定义

  1. 官网对象:
@Getter
@Setter
@EqualsAndHashCode
// 头背景设置成红色 IndexedColors.RED.getIndex()
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 10)
// 头字体设置成20
@HeadFontStyle(fontHeightInPoints = 20)
// 内容的背景设置成绿色 IndexedColors.GREEN.getIndex()
@ContentStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 17)
// 内容字体设置成20
@ContentFontStyle(fontHeightInPoints = 20)
public class DemoStyleData {
    // 字符串的头背景设置成粉红 IndexedColors.PINK.getIndex()
    @HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 14)
    // 字符串的头字体设置成20
    @HeadFontStyle(fontHeightInPoints = 30)
    // 字符串的内容的背景设置成天蓝 IndexedColors.SKY_BLUE.getIndex()
    @ContentStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 40)
    // 字符串的内容字体设置成20
    @ContentFontStyle(fontHeightInPoints = 30)
    @ExcelProperty("字符串标题")
    private String string;
    @ExcelProperty("日期标题")
    private Date date;
    @ExcelProperty("数字标题")
    private Double doubleData;
}/**
 * 样式的数据类
 *
 * @author Jiaju Zhuang
 **/
@Data
// 头背景设置成红色 IndexedColors.RED.getIndex()
@HeadStyle(fillPatternType = FillPatternType.SOLID_FOREGROUND, fillForegroundColor = 10)
// 头字体设置成20
@HeadFontStyle(fontHeightInPoints = 20)
// 内容的背景设置成绿色 IndexedColors.GREEN.getIndex()
@ContentStyle(fillPatternType = FillPatternType.SOLID_FOREGROUND, fillForegroundColor = 17)
// 内容字体设置成20
@ContentFontStyle(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(fontHeightInPoints = 30)
    @ExcelProperty("字符串标题")
    private String string;
    @ExcelProperty("日期标题")
    private Date date;
    @ExcelProperty("数字标题")
    private Double doubleData;
}
  1. 官网示例:
    /**
     * 注解形式自定义样式
     * 

* 1. 创建excel对应的实体对象 参照{@link DemoStyleData} *

* 3. 直接写即可 * * @since 2.2.0-beta1 */ @Test public void annotationStyleWrite() { String fileName = TestFileUtil.getPath() + "annotationStyleWrite" + System.currentTimeMillis() + ".xlsx"; // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 EasyExcel.write(fileName, DemoStyleData.class).sheet("模板").doWrite(data()); }

3.8 注解形式自定义样式

  1. 官网对象:
@Getter
@Setter
@EqualsAndHashCode
// 头背景设置成红色 IndexedColors.RED.getIndex()
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 10)
// 头字体设置成20
@HeadFontStyle(fontHeightInPoints = 20)
// 内容的背景设置成绿色 IndexedColors.GREEN.getIndex()
@ContentStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 17)
// 内容字体设置成20
@ContentFontStyle(fontHeightInPoints = 20)
public class DemoStyleData {
    // 字符串的头背景设置成粉红 IndexedColors.PINK.getIndex()
    @HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 14)
    // 字符串的头字体设置成20
    @HeadFontStyle(fontHeightInPoints = 30)
    // 字符串的内容的背景设置成天蓝 IndexedColors.SKY_BLUE.getIndex()
    @ContentStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 40)
    // 字符串的内容字体设置成20
    @ContentFontStyle(fontHeightInPoints = 30)
    @ExcelProperty("字符串标题")
    private String string;
    @ExcelProperty("日期标题")
    private Date date;
    @ExcelProperty("数字标题")
    private Double doubleData;
}/**
 * 样式的数据类
 *
 * @author Jiaju Zhuang
 **/
@Data
// 头背景设置成红色 IndexedColors.RED.getIndex()
@HeadStyle(fillPatternType = FillPatternType.SOLID_FOREGROUND, fillForegroundColor = 10)
// 头字体设置成20
@HeadFontStyle(fontHeightInPoints = 20)
// 内容的背景设置成绿色 IndexedColors.GREEN.getIndex()
@ContentStyle(fillPatternType = FillPatternType.SOLID_FOREGROUND, fillForegroundColor = 17)
// 内容字体设置成20
@ContentFontStyle(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(fontHeightInPoints = 30)
    @ExcelProperty("字符串标题")
    private String string;
    @ExcelProperty("日期标题")
    private Date date;
    @ExcelProperty("数字标题")
    private Double doubleData;
}
  1. 官网示例:
    /**
     * 注解形式自定义样式
     * 

* 1. 创建excel对应的实体对象 参照{@link DemoStyleData} *

* 3. 直接写即可 * * @since 2.2.0-beta1 */ @Test public void annotationStyleWrite() { String fileName = TestFileUtil.getPath() + "annotationStyleWrite" + System.currentTimeMillis() + ".xlsx"; // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 EasyExcel.write(fileName, DemoStyleData.class).sheet("模板").doWrite(data()); }

3.9 合并单元格

  1. 官网对象:
@Getter
@Setter
@EqualsAndHashCode
// 将第6-7行的2-3列合并成一个单元格
// @OnceAbsoluteMerge(firstRowIndex = 5, lastRowIndex = 6, firstColumnIndex = 1, lastColumnIndex = 2)
public class DemoMergeData {
    // 这一列 每隔2行 合并单元格
    @ContentLoopMerge(eachRow = 2)
    @ExcelProperty("字符串标题")
    private String string;
    @ExcelProperty("日期标题")
    private Date date;
    @ExcelProperty("数字标题")
    private Double doubleData;
}
  1. 官网示例:
   /**
     * 合并单元格
     * 

* 1. 创建excel对应的实体对象 参照{@link DemoData} {@link DemoMergeData} *

* 2. 创建一个merge策略 并注册 *

* 3. 直接写即可 * * @since 2.2.0-beta1 */ @Test public void mergeWrite() { // 方法1 注解 String fileName = TestFileUtil.getPath() + "mergeWrite" + System.currentTimeMillis() + ".xlsx"; // 在DemoStyleData里面加上ContentLoopMerge注解 // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 EasyExcel.write(fileName, DemoMergeData.class).sheet("模板").doWrite(data()); // 方法2 自定义合并单元格策略 fileName = TestFileUtil.getPath() + "mergeWrite" + System.currentTimeMillis() + ".xlsx"; // 每隔2行会合并 把eachColumn 设置成 3 也就是我们数据的长度,所以就第一列会合并。当然其他合并策略也可以自己写 LoopMergeStrategy loopMergeStrategy = new LoopMergeStrategy(2, 0); // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 EasyExcel.write(fileName, DemoData.class).registerWriteHandler(loopMergeStrategy).sheet("模板").doWrite(data()); }

3.10

4 导入(读)Excel

4.1 最简单的读

最简单的对象

@Getter
@Setter
@EqualsAndHashCode
public class DemoData {
    private String string;
    private Date date;
    private Double doubleData;
}

最简单的监听器

// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
@Slf4j
public class DemoDataListener implements ReadListener<DemoData> {

    /**
     * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 100;
    /**
     * 缓存的数据
     */
    private List<DemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
    /**
     * 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
     */
    private DemoDAO demoDAO;

    public DemoDataListener() {
        // 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
        demoDAO = new DemoDAO();
    }

    /**
     * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
     *
     * @param demoDAO
     */
    public DemoDataListener(DemoDAO demoDAO) {
        this.demoDAO = demoDAO;
    }

    /**
     * 这个每一条数据解析都会来调用
     *
     * @param data    one row value. Is is same as {@link AnalysisContext#readRowHolder()}
     * @param context
     */
    @Override
    public void invoke(DemoData data, AnalysisContext context) {
        log.info("解析到一条数据:{}", JSON.toJSONString(data));
        cachedDataList.add(data);
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (cachedDataList.size() >= BATCH_COUNT) {
            saveData();
            // 存储完成清理 list
            cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
        }
    }

    /**
     * 所有数据解析完成了 都会来调用
     *
     * @param context
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 这里也要保存数据,确保最后遗留的数据也存储到数据库
        saveData();
        log.info("所有数据解析完成!");
    }

    /**
     * 加上存储数据库
     */
    private void saveData() {
        log.info("{}条数据,开始存储数据库!", cachedDataList.size());
        demoDAO.save(cachedDataList);
        log.info("存储数据库成功!");
    }
}

持久层

/**
 * 假设这个是你的DAO存储。当然还要这个类让spring管理,当然你不用需要存储,也不需要这个类。
 **/
public class DemoDAO {
    public void save(List<DemoData> list) {
        // 如果是mybatis,尽量别直接调用多次insert,自己写一个mapper里面新增一个方法batchInsert,所有数据一次性插入
    }
}
    /**
     * 最简单的读
     * 

* 1. 创建excel对应的实体对象 参照{@link DemoData} *

* 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener} *

* 3. 直接读即可 */ @Test public void simpleRead() { // 写法1:JDK8+ ,不用额外写一个DemoDataListener // since: 3.0.0-beta1 String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; // 这里默认每次会读取100条数据 然后返回过来 直接调用使用数据就行 // 具体需要返回多少行可以在`PageReadListener`的构造函数设置 EasyExcel.read(fileName, DemoData.class, new PageReadListener<DemoData>(dataList -> { for (DemoData demoData : dataList) { log.info("读取到一条数据{}", JSON.toJSONString(demoData)); } })).sheet().doRead(); // 写法2: // 匿名内部类 不用额外写一个DemoDataListener fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭 EasyExcel.read(fileName, DemoData.class, new ReadListener<DemoData>() { /** * 单次缓存的数据量 */ public static final int BATCH_COUNT = 100; /** *临时存储 */ private List<DemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); @Override public void invoke(DemoData data, AnalysisContext context) { cachedDataList.add(data); if (cachedDataList.size() >= BATCH_COUNT) { saveData(); // 存储完成清理 list cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); } } @Override public void doAfterAllAnalysed(AnalysisContext context) { saveData(); } /** * 加上存储数据库 */ private void saveData() { log.info("{}条数据,开始存储数据库!", cachedDataList.size()); log.info("存储数据库成功!"); } }).sheet().doRead(); // 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去 // 写法3: fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭 EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead(); // 写法4 fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; // 一个文件一个reader try (ExcelReader excelReader = EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).build()) { // 构建一个sheet 这里可以指定名字或者no ReadSheet readSheet = EasyExcel.readSheet(0).build(); // 读取一个sheet excelReader.read(readSheet); } }

4.2 日期、数字或者自定义格式转换

对象

@Getter
@Setter
@EqualsAndHashCode
public class ConverterData {
    /**
     * 我自定义 转换器,不管数据库传过来什么 。我给他加上“自定义:”
     */
    @ExcelProperty(converter = CustomStringStringConverter.class)
    private String string;
    /**
     * 这里用string 去接日期才能格式化。我想接收年月日格式
     */
    @DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")
    private String date;
    /**
     * 我想接收百分比的数字
     */
    @NumberFormat("#.##%")
    private String doubleData;
}

监听器
参照:最简单的读的监听器 只是泛型变了

自定义转换器

public class CustomStringStringConverter implements Converter<String> {
    @Override
    public Class<?> supportJavaTypeKey() {
        return String.class;
    }

    @Override
    public CellDataTypeEnum supportExcelTypeKey() {
        return CellDataTypeEnum.STRING;
    }

    /**
     * 这里读的时候会调用
     *
     * @param context
     * @return
     */
    @Override
    public String convertToJavaData(ReadConverterContext<?> context) {
        return "自定义:" + context.getReadCellData().getStringValue();
    }

    /**
     * 这里是写的时候会调用 不用管
     *
     * @return
     */
    @Override
    public WriteCellData<?> convertToExcelData(WriteConverterContext<String> context) {
        return new WriteCellData<>(context.getValue());
    }

}

代码

    /**
     * 日期、数字或者自定义格式转换
     * 

* 默认读的转换器{@link DefaultConverterLoader#loadDefaultReadConverter()} *

1. 创建excel对应的实体对象 参照{@link ConverterData}.里面可以使用注解{@link DateTimeFormat}、{@link NumberFormat}或者自定义注解 *

2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link ConverterDataListener} *

3. 直接读即可 */ @Test public void converterRead() { String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; // 这里 需要指定读用哪个class去读,然后读取第一个sheet EasyExcel.read(fileName, ConverterData.class, new ConverterDataListener()) // 这里注意 我们也可以registerConverter来指定自定义转换器, 但是这个转换变成全局了, 所有java为string,excel为string的都会用这个转换器。 // 如果就想单个字段使用请使用@ExcelProperty 指定converter // .registerConverter(new CustomStringStringConverter()) // 读取sheet .sheet().doRead(); }

5 根据模板填充Excel

最简单的填充对象

@Getter
@Setter
@EqualsAndHashCode
public class FillData {
    private String name;
    private double number;
    private Date date;
}
    /**
     * 最简单的填充
     *
     * @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 = MapUtils.newHashMap();
        map.put("name", "张三");
        map.put("number", 5.2);
        EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(map);
    }

6 注解

注解 说明 使用方法
@ExcelProperty 这是最常用的一个注解,注解中有三个参数value,index,converter分别代表列名,列序号,数据转换方式,value和index只能二选一,通常不用设置converter 添加在属性上
@ColumnWith 用于设置列宽度的注解,注解中只有一个参数value,value的单位是字符长度,最大可以设置255个字符,因为一个excel单元格最大可以写入的字符个数就是255个字符。 添加在属性上
@ContentFontStyle 用于设置单元格内容字体格式的注解 添加在实体类或属性上,参数如下
@ContentLoopMerge 循环合并单元格,用于设置合并单元格的注解 添加在属性上
@OnceAbsoluteMerge 合并单元格 实体类上
@ContentRowHeight 用于设置行高 属性或实体类上 ,参数value 行高,-1代表自动行高
@ContentStyle 设置内容格式注解 实体类属性上
@HeadFontStyle 用于定制标题字体格式 实体类上或属性上
@HeadRowHeight 设置标题行行高 value 设置行高,-1代表自动行高
@HeadStyle 设置标题样式 实体类上
@ExcelIgnore 忽略这个字段 加在某个字段上,将忽略该字段
@ExcelIgnoreUnannotated 导出忽略某个字段不导出 在类的最上方加上@ExcelIgnoreUnannotated 注解即可;这个注解的意思是:忽略不加@ExcelProperty的字段进行导出

6.1 @ContentFontStyle参数

参数 含义
fontName 字体名称
fontHeightInPoints 字体高度
italic 是否斜体
strikeout 是否设置删除水平线
color 字体颜色
typeOffset 偏移量
underline 下划线
bold 是否加粗
charset 编码格式

6.2 @ContentLoopMerge

// 这一列 每隔2行 合并单元格
    @ContentLoopMerge(eachRow = 2)

6.3 @OnceAbsoluteMerge

// 将第6-7行的2-3列合并成一个单元格
@OnceAbsoluteMerge(firstRowIndex = 5, lastRowIndex = 6, firstColumnIndex = 1, lastColumnIndex = 2)

6.4 @ContentStyle参数

参数 含义
dataFormat 日期格式
hidden 设置单元格使用此样式隐藏
locked 设置单元格使用此样式锁定
quotePrefix 在单元格前面增加`符号,数字或公式将以字符串形式展示
horizontalAlignment 设置是否水平居中
wrapped 设置文本是否应换行。将此标志设置为true通过在多行上显示使单元格中的所有内容可见
verticalAlignment 设置是否垂直居中
rotation 设置单元格中文本旋转角度
indent 设置单元格中缩进文本的空格数
borderLeft 设置左边框的样式
borderRight 设置右边框样式
borderTop 设置上边框样式
borderBottom 设置下边框样式
leftBorderColor 设置左边框颜色
rightBorderColor 设置右边框颜色
topBorderColor 设置上边框颜色
bottomBorderColor 设置下边框颜色
fillPatternType 设置填充类型
fillBackgroundColor 设置背景色
fillForegroundColor 设置前景色
shrinkToFit 设置自动单元格自动大小

6.5 @HeadFontStyle 参数

参数 含义
fontName 设置字体名称(宋体,微软雅黑)
fontHeightInPoints 设置字体高度
italic 设置字体是否斜体
strikeout 是否设置删除线
color 设置字体颜色
typeOffset 设置偏移量
underline 设置下划线(可设置值 1或2,代表下划线条数)
charset 设置字体编码
bold 设置字体是否加粗

6. @HeadStyle参数

参数 含义
dataFormat 日期格式
hidden 设置单元格使用此样式隐藏
locked 设置单元格使用此样式锁定
quotePrefix 在单元格前面增加`符号,数字或公式将以字符串形式展示
horizontalAlignment 设置是否水平居中
wrapped 设置文本是否应换行。将此标志设置为true通过在多行上显示使单元格中的所有内容可见
verticalAlignment 设置是否垂直居中
rotation 设置单元格中文本旋转角度
indent 设置单元格中缩进文本的空格数
borderLeft 设置左边框的样式
borderRight 设置右边框样式
borderTop 设置上边框样式
borderBottom 设置下边框样式
leftBorderColor 设置左边框颜色
rightBorderColor 设置右边框颜色
topBorderColor 设置上边框颜色
bottomBorderColor 设置下边框颜色
fillPatternType 设置填充类型
fillBackgroundColor 设置背景色
fillForegroundColor 设置前景色
shrinkToFit 设置自动单元格自动大小

7 Demo

你可能感兴趣的:(java,springboot,EasyExcel,excel,java)