5.常用API及注解
5.1 常用类
- EasyExcel:入口类,用于构建开始各种操作;
- ExcelReaderBuilder:构建出一个ReadWorkbook对象,即一个工作簿对象,对应的是一个Excel文件;
- ExcelWriterBuilder:构建出一个WriteWorkbook对象,即一个工作簿对象,对应的是一个Excel文件;
- ExcelReaderSheetBuilder:构建出一个ReadSheet对象,即一个工作表的对象,对应的Excel中的每个sheet,一个工作簿可以有多个工作表;
- ExcelWriterSheetBuilder:构建出一WriteSheet对象,即一个工作表的对象,对应的Excel中的每个sheet,一个工作簿可以有多个工作表;
- ReadListener:在每一行读取完毕后都会调用ReadListener来处理数据,我们可以把调用service的代码可以写在其
invoke
方法内部; - WriteHandler:在每一个操作包括创建单元格、创建表格等都会调用WriteHandler来处理数据,对使用者透明不可见;
所有配置都是继承的。Workbook的配置会被Sheet继承。所以在用EasyExcel设置参数的时候,在EasyExcel…sheet()方法之前作用域是整个sheet,之后针对单个sheet。
5.2 读取时的注解
🍀 @ExcelProperty
使用位置:标准作用在成员变量上,吧实体类中属性和excel表中列关联起来。
可选属性:
属性名 | 含义 | 说明 |
index | 对应Excel表中的列数 | 默认-1,建议指定时从0开始 |
value | 对应Excel表中的列头 | |
converter | 成员变量转换器 | 自定义转换器需要实Converter接口 |
使用效果:index属性可以指定当前字段对应excel中的哪一列,可以根据列名value去匹配,也可以不写。
如果不使用@ExcelProperty注解,成员变量从上到下的顺序,对应表格中从左到右的顺序;
**使用建议:**要么全部不写,要么全部用index,要么全部用名字去匹配,尽量不要三个混着用。
🍀 @ExcelIgnore
标注在成员变量上,默认所有字段都会和excel去匹配,加了这个注解会忽略该字段
🍀 @DateTimeFormat
标注在成员变量上,日期转换,代码中用String类型的成员变量
去接收excel中日期格式的数据
会调用这个注解。里面的value
参照java.text.SimpleDateFormat
/** * 学生出生日期 */ @ExcelProperty("出生日期") @ColumnWidth(20) @DateTimeFormat("yyyy-MM-dd") private Date birthday;
🍀 @NumberFormat
标注在成员变量上,数字转换,代码中用String类型的成员变量
去接收excel数字格式的数据
会调用这个注解。里面的value
参照java.text.DecimalFormat
🍀 @ExcelIgnoreUnannotated
标注在类上。
不标注该注解时,默认类中所有成员变量都会参与读写,无论是否在成员变量上加了@ExcelProperty
的注解。
标注该注解后,类中的成员变量如果没有标注 @ExcelProperty
注解将不会参与读写。
5.3 读取时通用参数
ReadWorkbook
,ReadSheet
都会有的参数,如果为空,默认使用上级。
converter
转换器,默认加载了很多转换器。也可以自定义。readListener
监听器,在读取数据的过程中会不断的调用监听器。headRowNumber
指定需要读表格的 列头行数。默认有一行头,也就是认为第二行开始起为数据。head
与clazz
二选一。读取文件头对应的列表,会根据列表匹配数据。建议使用class,就是文件中每一行数据对应的代码中的实体类型。clazz
与head
二选一。读取文件的头对应的class,也可以使用注解。如果两个都不指定,则会读取全部数据。autoTrim
字符串、表头等数据自动trimpassword
读的时候是否需要使用密码
5.4 ReadWorkbook(工作簿对象)参数
excelType
当前excel的类型,读取时会自动判断,无需设置。inputStream
与file
二选一。建议使用file。file
与inputStream
二选一。读取文件的文件。autoCloseStream
自动关闭流。readCache
默认小于5M用 内存,超过5M会使用EhCache
,不建议使用这个参数。useDefaultListener
@since 2.1.4
默认会加入ModelBuildEventListener
来帮忙转换成传入class
的对象,设置成false
后将不会协助转换对象,自定义的监听器会接收到Map<Integer,CellData>
对象,如果还想继续接听到class
对象,请调用readListener
方法,加入自定义的beforeListener
、ModelBuildEventListener
、 自定义的afterListener
即可。
5.5 ReadSheet(工作表对象)参数
sheetNo
需要读取Sheet的编号,建议使用这个来指定读取哪个SheetsheetName
根据名字去匹配Sheet,excel 2003不支持根据名字去匹配
5.6 写入时的注解
5.6.1 @ExcelProperty
使用位置:标准作用在成员变量上
可选属性:
属性名 | 含义 | 说明 |
index | 对应Excel表中的列数 | 默认-1,指定时建议从0开始 |
value | 对应Excel表中的列头 | |
converter | 成员变量转换器 | 自定义转换器需要实Converter接口 |
使用效果:
index
指定写到第几列,如果不指定则根据成员变量位置排序;value
指定写入的列头,如果不指定则使用成员变量的名字作为列头;
如果要设置复杂的头,可以为value指定多个值。
import com.alibaba.excel.annotation.ExcelIgnore; import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.format.DateTimeFormat; 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.Data; import lombok.NoArgsConstructor; import java.util.Date; /** * 学生实体类 * lombok:通过一个插件 + 一个依赖 ,就可以在编译的时候自动帮助生成实体类常用方法 * 注解 @ContentRowHeight():内容的行高 * 注解 @HeadRowHeight:表头的行高 * * @author 狐狸半面添 * @create 2023-02-26 14:56 */ @Data @NoArgsConstructor @AllArgsConstructor public class Student { /** * 学生姓名 */ @ExcelProperty(value = {"学员信息表", "学生姓名"}) @ColumnWidth(20) private String name; /** * 学生出生日期 */ @ExcelProperty(value = {"学员信息表", "出生日期"}) @ColumnWidth(20) @DateTimeFormat("yyyy-MM-dd") private Date birthday; /** * index 从0开始 * 学生性别 */ @ExcelProperty(value = {"学员信息表", "学员性别"}) @ColumnWidth(20) private String gender; /** * id */ @ExcelIgnore private String id; }
5.6.2 其他注解
基本和读取时一致
@ContentRowHeight()
:标注在类上或属性上,指定内容行高@HeadRowHeight()
:标注在类上或属性上,指定列头行高@ColumnWidth()
:标注在类上或属性上,指定列宽@ExcelIgnore
:默认所有字段都会写入excel,这个注解会忽略这个字段DateTimeFormat
:日期转换,将Date
写到excel会调用这个注解。里面的value
参照java.text.SimpleDateFormat
NumberFormat
:数字转换,用Number
写excel会调用这个注解。里面的value
参照java.text.DecimalFormat
ExcelIgnoreUnannotated
:默认不加ExcelProperty
的注解的都会参与读写,加了不会参与
5.7 写入时通用参数
WriteWorkbook
、WriteSheet
都会有的参数,如果为空,默认使用上级。
converter
转换器,默认加载了很多转换器。也可以自定义。writeHandler
写的处理器。可以实现WorkbookWriteHandler
,SheetWriteHandler
,RowWriteHandler
,CellWriteHandler
,在写入excel的不同阶段会调用,对使用者透明不可见。relativeHeadRowIndex
距离多少行后开始。也就是开头空几行needHead
是否导出头head
与clazz
二选一。写入文件的头列表,建议使用class。clazz
与head
二选一。写入文件的头对应的class,也可以使用注解。autoTrim
字符串、表头等数据自动trim
5.8 WriteWorkbook(工作簿对象)参数
excelType
当前excel的类型,默认为xlsx
outputStream
与file
二选一。写入文件的流file
与outputStream
二选一。写入的文件templateInputStream
模板的文件流templateFile
模板文件autoCloseStream
自动关闭流。password
写的时候是否需要使用密码useDefaultStyle
写的时候是否是使用默认头
5.9 WriteSheet(工作表对象)参数
sheetNo
需要写入的编号。默认0sheetName
需要些的Sheet名称,默认同sheetNo
6.easyexcel工具类
6.1 excel通用读取监听类
import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.event.AnalysisEventListener; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import java.util.ArrayList; import java.util.List; /** * excel通用读取监听类 * * @author 狐狸半面添 * @create 2023-02-26 15:10 */ @Slf4j @Getter @NoArgsConstructor public class ExcelListener<T> extends AnalysisEventListener<T> { /** * 自定义用于暂时存储data 可以通过实例获取该值 */ private final List<T> dataList = new ArrayList<>(); /** * 每解析一行都会回调invoke()方法 * * @param data 每一行的数据 */ @Override public void invoke(T data, AnalysisContext context) { dataList.add(data); log.info("读取的一条信息:{}", data); } @Override public void doAfterAllAnalysed(AnalysisContext context) { log.info("{}条数据,解析完成", dataList.size()); } }
6.2 工具类
import com.alibaba.excel.EasyExcel; import com.alibaba.excel.ExcelReader; import com.alibaba.excel.ExcelWriter; import com.alibaba.excel.read.metadata.ReadSheet; import com.alibaba.excel.support.ExcelTypeEnum; import com.alibaba.excel.write.builder.ExcelWriterBuilder; import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder; import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.metadata.style.WriteCellStyle; import com.alibaba.excel.write.metadata.style.WriteFont; import com.alibaba.excel.write.style.HorizontalCellStyleStrategy; import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; import com.fox.easyexcel.listener.ExcelListener; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.poi.ss.usermodel.BorderStyle; import org.apache.poi.ss.usermodel.HorizontalAlignment; import org.apache.poi.ss.usermodel.IndexedColors; import org.apache.poi.ss.usermodel.VerticalAlignment; import org.springframework.util.StringUtils; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URLEncoder; import java.util.List; import java.util.Set; /** * @author 狐狸半面添 * @create 2023-02-26 21:50 */ public class EasyExcelUtils { private static final Log log = LogFactory.getLog(EasyExcelUtils.class); /** * 单sheet版本Excel读取 * 从Excel中读取文件,读取的文件是一个DTO类 * * @param inputStream 文件流 * @param clazz 行数据类型 */ public static <T> List<T> readExcelOneSheet(InputStream inputStream, final Class<?> clazz) { // 1.创建监听类 ExcelListener<T> listener = new ExcelListener<>(); // 2.构建工作簿对象的输入流 ExcelReader excelReader = EasyExcel.read(inputStream, clazz, listener).build(); // 3.构建工作表对象的输入流,默认是第一张工作表 ReadSheet readSheet = EasyExcel.readSheet(0).build(); // 4.读取信息,每读取一行都会调用监听类的 invoke 方法 excelReader.read(readSheet); // 5.关闭流,如果不关闭,读的时候会创建临时文件,到时磁盘会崩的 excelReader.finish(); return listener.getDataList(); } /** * 多sheet版本Excel读取 * * @param <T> 行数据的类型 * @param filePath 文件路径 * @param clazz 行数据的类型 * @return 所有信息 */ public static <T> List<T> readExcelAllSheet(String filePath, final Class<?> clazz) { ExcelListener<T> listener = new ExcelListener<>(); // 读取全部sheet // 这里需要注意 ExcelListener的doAfterAllAnalysed 会在每个sheet读取完毕后调用一次。然后所有sheet都会往同一个DemoDataListener里面写 EasyExcel.read(filePath, clazz, listener).doReadAll(); return listener.getDataList(); } /** * 网页上的下载导出,只有一个工作表 * * @param fileName 文件名 * @param clazz 类的字节码文件,行数据的类型 * @param dataList 导出的数据 * @param sheetName 工作表名 * @param response 响应体 * @throws IOException 异常对象 */ public static void writeWeb(String fileName, final Class<?> clazz, List<?> dataList, String sheetName, HttpServletResponse response) throws IOException { // 1.指定响应体内容类型 response.setContentType("application/vnd.ms-excel"); // 2.指定编码方式 response.setCharacterEncoding("utf-8"); // 3.URLEncoder.encode可以防止中文乱码:import java.net.URLEncoder fileName = URLEncoder.encode(fileName, "UTF-8"); // 4.指定响应标头 response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + fileName + ".xlsx"); // 5.获取工作簿对象的输出流 ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).build(); // 6.设置工作表的名称 if (!StringUtils.hasText(sheetName)) { sheetName = "sheet1"; } // 7.指定写用哪个class去写 WriteSheet writeSheet = EasyExcel.writerSheet(0, sheetName).head(clazz).build(); // 8.将 dataList 中的数据逐行写入工作表中 excelWriter.write(dataList, writeSheet); // 9.finish关闭流 excelWriter.finish(); // 10.关闭流 response.getOutputStream().close(); } public static <T> void writeExcelList(HttpServletResponse response, List<List<T>> data, String fileName, Class<?> clazz, String sheetName) throws Exception { OutputStream out = getOutputStream(fileName, response); ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(out, clazz).excelType(ExcelTypeEnum.XLSX).registerWriteHandler(getDefaultHorizontalCellStyleStrategy()); ExcelWriter excelWriter = excelWriterBuilder.build(); ExcelWriterSheetBuilder excelWriterSheetBuilder; WriteSheet writeSheet; for (int i = 1; i <= data.size(); i++) { excelWriterSheetBuilder = new ExcelWriterSheetBuilder(excelWriter); excelWriterSheetBuilder.sheetNo(i); excelWriterSheetBuilder.sheetName(sheetName + i); writeSheet = excelWriterSheetBuilder.build(); excelWriter.write(data.get(i - 1), writeSheet); } excelWriter.finish(); out.close(); } private static OutputStream getOutputStream(String fileName, HttpServletResponse response) throws Exception { fileName = URLEncoder.encode(fileName, "UTF-8"); // response.setContentType("application/vnd.ms-excel"); // .xls // .xlsx response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setCharacterEncoding("utf8"); response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + fileName + ".xlsx"); return response.getOutputStream(); } /** * 获取默认表头内容的样式 * * @return */ private static HorizontalCellStyleStrategy getDefaultHorizontalCellStyleStrategy() { /** 表头样式 **/ WriteCellStyle headWriteCellStyle = new WriteCellStyle(); // 背景色(浅灰色) // 可以参考:https://www.cnblogs.com/vofill/p/11230387.html headWriteCellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); // 字体大小 WriteFont headWriteFont = new WriteFont(); headWriteFont.setFontHeightInPoints((short) 10); headWriteCellStyle.setWriteFont(headWriteFont); //设置表头居中对齐 headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER); /** 内容样式 **/ WriteCellStyle contentWriteCellStyle = new WriteCellStyle(); // 内容字体样式(名称、大小) WriteFont contentWriteFont = new WriteFont(); contentWriteFont.setFontName("宋体"); contentWriteFont.setFontHeightInPoints((short) 10); contentWriteCellStyle.setWriteFont(contentWriteFont); // //设置内容垂直居中对齐 // contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER); // //设置内容水平居中对齐 // contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER); // 设置边框样式 contentWriteCellStyle.setBorderLeft(BorderStyle.THIN); contentWriteCellStyle.setBorderTop(BorderStyle.THIN); contentWriteCellStyle.setBorderRight(BorderStyle.THIN); contentWriteCellStyle.setBorderBottom(BorderStyle.THIN); // 头样式与内容样式合并 return new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle); } /** * 导出 Excel到指定目录 :单个 sheet,带表头, * * @param tableData * @param fileName 导出的路径+文件名 例如: file/test.xlsx * @param sheetName 导入文件的 sheet 名 * @throws Exception */ public static void writeExcelAutoColumnWidth(String fileName, List<?> tableData, String sheetName, Class<?> clazz) throws Exception { // 根据用户传入字段 假设我们要忽略 date EasyExcel.write(fileName, clazz) .sheet(sheetName) .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) .doWrite(tableData); } /** * 导出 Excel到指定目录 :单个 sheet,带表头, * * @param fileName 导出的路径+文件名 例如: file/test.xlsx * @param tableData */ public static void writeExcelWithOneSheet1(String fileName, List<?> tableData, String sheetName, Class<?> clazz, Set<String> excludeColumnFiledNames) { // 根据用户传入字段 假设我们要忽略 date EasyExcel.write(fileName, clazz) .excludeColumnFiledNames(excludeColumnFiledNames) .sheet(sheetName) .registerWriteHandler(styleWrite(false)) .doWrite(tableData); } public static HorizontalCellStyleStrategy styleWrite(boolean isWrapped) { // 头的策略 WriteCellStyle headWriteCellStyle = new WriteCellStyle(); // 背景设置为红色 // headWriteCellStyle.setFillForegroundColor(IndexedColors.RED.getIndex()); WriteFont headWriteFont = new WriteFont(); headWriteFont.setFontHeightInPoints((short) 18); 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) 11); //设置 自动换行 contentWriteCellStyle.setWrapped(isWrapped); //设置 垂直居中 contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER); contentWriteCellStyle.setWriteFont(contentWriteFont); // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现 return new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle); // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 //EasyExcel.write(fileName, DemoData.class).registerWriteHandler(horizontalCellStyleStrategy).sheet("模板") // .doWrite(data()); } }