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.SimpleDateFormatNumberFormat:数字转换,用Number写excel会调用这个注解。里面的value参照java.text.DecimalFormatExcelIgnoreUnannotated:默认不加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的类型,默认为xlsxoutputStream与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()); } }
