【Alibaba工具型技术系列】「EasyExcel技术专题」实战技术针对于项目中常用的Excel操作指南

这里写目录标题

      • EasyExcel教程
        • Maven依赖
      • EasyExcel API分析介绍
      • EasyExcel 注解
      • 通用参数
        • ReadWorkbook(理解成excel对象)参数
        • ReadSheet(就是excel的一个Sheet)参数
        • 注解
        • 参数
          • 通用参数
        • WriteWorkbook(理解成excel对象)参数
        • WriteSheet(就是excel的一个Sheet)参数
        • WriteTable(就把excel的一个Sheet,一块区域看一个table)参数
        • EasyExcel用法指南
          • 简单的读取excel文件
          • 简单的写入excel文件
          • Web上传与下载
        • 详解读取Excel
          • 对象模型
          • 监听器
          • 代码
          • 读取多个sheet
          • 自定义格式转换
        • 多行头
        • 读取表头数据
      • 异常处理
        • 导出指定的列
        • 调整指定列顺序
        • 复杂头写入
        • 图片导出
        • 列宽、行高
        • 动态表头
        • 合并单元格
        • web数据写出
        • 模板格式导出
          • 简单的Excel模板
          • 实现模板填充
        • 复杂的填充
      • 总结一个工具类
      • 参考资料

EasyExcel教程

本文使用的技术是Alibaba集团开源的EasyExcel技术,该技术是针对Apache POI技术的封装和优化,主要解决了POI技术的耗内存问题,并且提供了较好的API使用。

  • 使用步骤繁琐
  • 动态写出Excel操作非常麻烦
  • 对于新手来说,很难在短时间内上手
  • 读写时需要占用较大的内容,当数据量大时容器发生OOM
Maven依赖
  <!-- easyexcel 依赖 --><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.7</version></dependency>

EasyExcel API分析介绍

  • Ø EasyExcel入口类,用于构建开始各种操作

  • Ø ExcelReaderBuilder ExcelWriterBuilder 构建出一个 ReadWorkbook WriteWorkbook,可以理解成一个excel对象,一个excel只要构建一个

  • Ø ExcelReaderSheetBuilder ExcelWriterSheetBuilder 构建出一个 ReadSheet WriteSheet对象,可以理解成excel里面的一页,每一页都要构建一个

  • Ø ReadListener在每一行读取完毕后都会调用ReadListener来处理数据

  • Ø WriteHandler在每一个操作包括创建单元格、创建表格等都会调用WriteHandler来处理数据

  • Ø 所有配置都是继承的,Workbook的配置会被Sheet继承,所以在用EasyExcel设置参数的时候,在EasyExcel…sheet()方法之前作用域是整个sheet,之后针对单个sheet

EasyExcel 注解

  • Ø ExcelProperty 指定当前字段对应excel中的那一列。可以根据名字或者Index去匹配。当然也可以不写,默认第一个字段就是index=0,以此类推。千万注意,要么全部不写,要么全部用index,要么全部用名字去匹配。千万别三个混着用,除非你非常了解源代码中三个混着用怎么去排序的。

  • Ø ExcelIgnore 默认所有字段都会和excel去匹配,加了这个注解会忽略该字段

  • Ø DateTimeFormat 日期转换,用String去接收excel日期格式的数据会调用这个注解。里面的value参照java.text.SimpleDateFormat

  • Ø NumberFormat 数字转换,用String去接收excel数字格式的数据会调用这个注解。里面的value参照java.text.DecimalFormat

  • Ø ExcelIgnoreUnannotated 默认不加ExcelProperty 的注解的都会参与读写,加了不会参与

通用参数

  • Ø ReadWorkbook,ReadSheet 都会有的参数,如果为空,默认使用上级。

  • Ø converter 转换器,默认加载了很多转换器。也可以自定义。

  • Ø readListener 监听器,在读取数据的过程中会不断的调用监听器。

  • Ø headRowNumber 需要读的表格有几行头数据。默认有一行头,也就是认为第二行开始起为数据。

  • Ø head 与clazz二选一。读取文件头对应的列表,会根据列表匹配数据,建议使用class。

  • Ø clazz 与head二选一。读取文件的头对应的class,也可以使用注解。如果两个都不指定,则会读取全部数据。

  • Ø autoTrim 字符串、表头等数据自动trim

  • Ø password 读的时候是否需要使用密码

ReadWorkbook(理解成excel对象)参数
  • Ø excelType 当前excel的类型 默认会自动判断

  • Ø inputStream 与file二选一。读取文件的流,如果接收到的是流就只用,不用流建议使用file参数。因为使用了inputStream easyexcel会帮忙创建临时文件,最终还是file

  • Ø file 与inputStream二选一。读取文件的文件。

  • Ø autoCloseStream 自动关闭流。

  • Ø readCache 默认小于5M用 内存,超过5M会使用 EhCache,这里不建议使用这个参数。

ReadSheet(就是excel的一个Sheet)参数
  • Ø sheetNo 需要读取Sheet的编码,建议使用这个来指定读取哪个Sheet

  • Ø sheetName 根据名字去匹配Sheet,excel 2003不支持根据名字去匹配

注解
  • Ø ExcelProperty index 指定写到第几列,默认根据成员变量排序。value指定写入的名称,默认成员变量的名字,多个value可以参照快速开始中的复杂头

  • Ø ExcelIgnore 默认所有字段都会写入excel,这个注解会忽略这个字段

  • Ø DateTimeFormat 日期转换,将Date写到excel会调用这个注解。里面的value参照java.text.SimpleDateFormat

  • Ø NumberFormat 数字转换,用Number写excel会调用这个注解。里面的value参照java.text.DecimalFormat

  • Ø ExcelIgnoreUnannotated 默认不加ExcelProperty 的注解的都会参与读写,加了不会参与

参数
通用参数
  • Ø WriteWorkbook,WriteSheet ,WriteTable都会有的参数,如果为空,默认使用上级。

  • Ø converter 转换器,默认加载了很多转换器。也可以自定义。

  • Ø writeHandler 写的处理器。可以实现WorkbookWriteHandler,SheetWriteHandler,RowWriteHandler,CellWriteHandler,在写入excel的不同阶段会调用

  • Ø relativeHeadRowIndex 距离多少行后开始。也就是开头空几行

  • Ø needHead 是否导出头

  • Ø head 与clazz二选一。写入文件的头列表,建议使用class。

  • Ø clazz 与head二选一。写入文件的头对应的class,也可以使用注解。

  • Ø autoTrim 字符串、表头等数据自动trim

WriteWorkbook(理解成excel对象)参数
  • Ø excelType 当前excel的类型 默认xlsx

  • Ø outputStream 与file二选一。写入文件的流

  • Ø file 与outputStream二选一。写入的文件

  • Ø templateInputStream 模板的文件流

  • Ø templateFile 模板文件

  • Ø autoCloseStream 自动关闭流。

  • Ø password 写的时候是否需要使用密码

  • Ø useDefaultStyle 写的时候是否是使用默认头

WriteSheet(就是excel的一个Sheet)参数
  • Ø sheetNo 需要写入的编码。默认0

  • Ø sheetName 需要些的Sheet名称,默认同sheetNo

WriteTable(就把excel的一个Sheet,一块区域看一个table)参数
  • Ø tableNo 需要写入的编码。默认0
EasyExcel用法指南
简单的读取excel文件
public void read() {String fileName = "demo.xlsx";// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭// 参数一:读取的excel文件路径// 参数二:读取sheet的一行,将参数封装在DemoData实体类中// 参数三:读取每一行的时候会执行DemoDataListener监听器EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();
}
简单的写入excel文件
@Test
public void simpleWrite() {String fileName = "demo.xlsx";// 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭// 如果这里想使用03 则 传入excelType参数即可// 参数一:写入excel文件路径// 参数二:写入的数据类型是DemoData// data()方法是写入的数据,结果是List<DemoData>集合EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());
}
Web上传与下载
/**excel文件的下载
*/
@GetMapping("download")
public void download(HttpServletResponse response) throws IOException {response.setContentType("application/vnd.ms-excel");response.setCharacterEncoding("utf-8");response.setHeader("Content-disposition", "attachment;filename=demo.xlsx");EasyExcel.write(response.getOutputStream(), DownloadData.class).sheet("模板").doWrite(data());
}/**excel文件的上传
*/
@PostMapping("upload")
@ResponseBody
public String upload(MultipartFile file) throws IOException {EasyExcel.read(file.getInputStream(), DemoData.class, new DemoDataListener()).sheet().doRead();return "success";
}
详解读取Excel
对象模型
// 如果没有特殊说明,下面的案例将默认使用这个实体类
public class DemoData {/*** 强制读取第三个 这里不建议 index 和 name 同时用,要么一个对象只用index,要么一个对象只用name去匹配*/@ExcelProperty(index = 2)//我想接收百分比的数字@NumberFormat("#.##%")@ExcelProperty(value="浮点数标题", converter = CustomStringConverter.class)private Double doubleData;/*** 用名字去匹配,这里需要注意,如果名字重复,会导致只有一个字段读取到数据*/@ExcelProperty(value="字符串标题", converter = CustomStringConverter.class)// converter属性定义自己的字符串转换器private String string;@DateTimeFormat("yyyy年MM月dd日 HH时mm分ss秒")@ExcelProperty("日期标题")//这里用string 去接日期才能格式化private Date date;
}
监听器
// 如果没有特殊说明,下面的案例将默认使用这个监听器
public class DemoDataListener extends AnalysisEventListener<DemoData> {List<DemoData> list = new ArrayList<DemoData>();/*** 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来*/public DemoDataListener() {}/*** 这个每一条数据解析都会来调用** @param data* @param context*/@Overridepublic void invoke(DemoData data, AnalysisContext context) {System.out.println("解析到一条数据:{}", JSON.toJSONString(data));list.add(data);}/*** 所有数据解析完成了 都会来调用** @param context*/@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {System.out.println(JSON.toJSONString(list));}
}
代码
@Test
public void simpleRead() {// 写法1:String fileName = "demo.xlsx";// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();// 写法2:fileName = "demo.xlsx";ExcelReader excelReader = EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).build();ReadSheet readSheet = EasyExcel.readSheet(0).build();excelReader.read(readSheet);// 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的excelReader.finish();
}
读取多个sheet
@Test
public void repeatedRead() {String fileName = "demo.xlsx";// 读取全部sheet// 这里需要注意 DemoDataListener的doAfterAllAnalysed 会在每个sheet读取完毕后调用一次。然后所有sheet都会往同一个DemoDataListener里面写EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).doReadAll();// 读取部分sheetfileName = "demo.xlsx";ExcelReader excelReader = EasyExcel.read(fileName).build();// 这里为了简单 所以注册了 同样的head 和Listener 自己使用功能必须不同的Listener// readSheet参数设置读取sheet的序号ReadSheet readSheet1 =EasyExcel.readSheet(0).head(DemoData.class).registerReadListener(new DemoDataListener()).build();ReadSheet readSheet2 =EasyExcel.readSheet(1).head(DemoData.class).registerReadListener(new DemoDataListener()).build();// 这里注意 一定要把sheet1 sheet2 一起传进去,不然有个问题就是03版的excel 会读取多次,浪费性能excelReader.read(readSheet1, readSheet2);// 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的excelReader.finish();
}
自定义格式转换
public class CustomStringStringConverter implements Converter<String> {@Overridepublic Class supportJavaTypeKey() {return String.class;}@Overridepublic CellDataTypeEnum supportExcelTypeKey() {return CellDataTypeEnum.STRING;}/*** 这里读的时候会调用** @param cellData*            NotNull* @param contentProperty*            Nullable* @param globalConfiguration*            NotNull* @return*/@Overridepublic String convertToJavaData(CellData cellData, ExcelContentProperty contentProperty,GlobalConfiguration globalConfiguration) {return "自定义:" + cellData.getStringValue();}/*** 这里是写的时候会调用 不用管** @param value*            NotNull* @param contentProperty*            Nullable* @param globalConfiguration*            NotNull* @return*/@Overridepublic CellData convertToExcelData(String value, ExcelContentProperty contentProperty,GlobalConfiguration globalConfiguration) {return new CellData(value);}
}
多行头
@Test
public void complexHeaderRead() {String fileName = "demo.xlsx";// 这里 需要指定读用哪个class去读,然后读取第一个sheet EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet()// 这里可以设置1,因为头就是一行。如果多行头,可以设置其他值。不传入默认1行.headRowNumber(1).doRead();
}
读取表头数据

覆盖监听器invokeHeadMap方法

/*** 这里会一行行的返回头* 监听器只需要重写这个方法就可以读取到头信息* @param headMap* @param context*/
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {LOGGER.info("解析到一条头数据:{}", JSON.toJSONString(headMap));
}

异常处理

覆盖监听器onException方法

/**
* 监听器实现这个方法就可以在读取数据的时候获取到异常信息
*/
@Override
public void onException(Exception exception, AnalysisContext context) {LOGGER.error("解析失败,但是继续解析下一行:{}", exception.getMessage());// 如果是某一个单元格的转换异常 能获取到具体行号// 如果要获取头的信息 配合invokeHeadMap使用if (exception instanceof ExcelDataConvertException) {ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;LOGGER.error("第{}行,第{}列解析异常", excelDataConvertException.getRowIndex(),excelDataConvertException.getColumnIndex());}
}
导出指定的列
@Test
public void excludeOrIncludeWrite() {String fileName = "excludeOrIncludeWrite" + System.currentTimeMillis() + ".xlsx";// 忽略 date 不导出Set<String> excludeColumnFiledNames = new HashSet<String>();excludeColumnFiledNames.add("date");// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭EasyExcel.write(fileName, DemoData.class).excludeColumnFiledNames(excludeColumnFiledNames).sheet("忽略date").doWrite(data());fileName = "excludeOrIncludeWrite" + System.currentTimeMillis() + ".xlsx";// 根据用户传入字段 假设我们只要导出 dateSet<String> includeColumnFiledNames = new HashSet<String>();includeColumnFiledNames.add("date");// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭EasyExcel.write(fileName, DemoData.class).includeColumnFiledNames(includeColumnFiledNames).sheet("导出date").doWrite(data());
}
调整指定列顺序
public class IndexData {/*** 导出的excel第二列和第四列将空置*/@ExcelProperty(value = "字符串标题", index = 0)private String string;@ExcelProperty(value = "日期标题", index = 2)private Date date;@ExcelProperty(value = "浮点数标题", index = 4)private Double doubleData;
}
复杂头写入
public class ComplexHeadData {/*** 主标题 将整合为一个单元格效果如下:* —————————————————————————* |          主标题        |* —————————————————————————* |字符串标题|日期标题|数字标题|* —————————————————————————*/@ExcelProperty({"主标题", "字符串标题"})private String string;@ExcelProperty({"主标题", "日期标题"})private Date date;@ExcelProperty({"主标题", "数字标题"})private Double doubleData;
}

前面属于主标题,后面属于副标题

图片导出
@Data
@ContentRowHeight(200)
@ColumnWidth(200 / 8)
public class ImageData {// 图片导出方式有5种private File file;private InputStream inputStream;/*** 如果string类型 必须指定转换器,string默认转换成string,该转换器是官方支持的*/@ExcelProperty(converter = StringImageConverter.class)private String string;private byte[] byteArray;/*** 根据url导出 版本2.1.1才支持该种模式*/private URL url;
}@Test
public void imageWrite() throws Exception {String fileName = "imageWrite" + System.currentTimeMillis() + ".xlsx";// 如果使用流 记得关闭InputStream inputStream = null;try {List<ImageData> list = new ArrayList<ImageData>();ImageData imageData = new ImageData();list.add(imageData);String imagePath = "converter" + File.separator + "img.jpg";// 放入五种类型的图片 根据实际使用只要选一种即可imageData.setByteArray(FileUtils.readFileToByteArray(new File(imagePath)));imageData.setFile(new File(imagePath));imageData.setString(imagePath);inputStream = FileUtils.openInputStream(new File(imagePath));imageData.setInputStream(inputStream);imageData.setUrl(new URL("https://raw.githubusercontent.com/alibaba/easyexcel/master/src/test/resources/converter/img.jpg"));EasyExcel.write(fileName, ImageData.class).sheet().doWrite(list);} finally {if (inputStream != null) {inputStream.close();}}
}
列宽、行高
@Data
@ContentRowHeight(10)
@HeadRowHeight(20)
@ColumnWidth(25)
public class WidthAndHeightData {@ExcelProperty("字符串标题")private String string;@ExcelProperty("日期标题")private Date date;/*** 宽度为50,覆盖上面的宽度25*/@ColumnWidth(50)@ExcelProperty("数字标题")private Double doubleData;
}
  • @HeadRowHeight(value = 35) // 表头行高
  • @ContentRowHeight(value = 25) // 内容行高
  • @ColumnWidth(value = 50) // 列宽

此外还有,自适应宽度,但是这个不是特别精确

@Test
void contextLoads() {EasyExcel.write("自适应.xlsx", Student.class).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet().doWrite(getData());
}
动态表头
@Test
public void dynamicHeadWrite() {String fileName = "dynamicHeadWrite" + System.currentTimeMillis() + ".xlsx";EasyExcel.write(fileName)// 这里放入动态头.head(head()).sheet("模板")// 当然这里数据也可以用 List<List<String>> 去传入.doWrite(data());
}
// 动态表头的数据格式List<List<String>>
private List<List<String>> head() {List<List<String>> list = new ArrayList<List<String>>();List<String> head0 = new ArrayList<String>();head0.add("字符串" + System.currentTimeMillis());List<String> head1 = new ArrayList<String>();head1.add("数字" + System.currentTimeMillis());List<String> head2 = new ArrayList<String>();head2.add("日期" + System.currentTimeMillis());list.add(head0);list.add(head1);list.add(head2);return list;
}
合并单元格
 @Testpublic void mergeWrite() {String fileName = "mergeWrite" + System.currentTimeMillis() + ".xlsx";LoopMergeStrategy loopMergeStrategy = new LoopMergeStrategy(2, 0);EasyExcel.write(fileName, DemoData.class).registerWriteHandler(loopMergeStrategy).sheet("合并单元格").doWrite(data());}
  • 每隔2行会合并 把eachColumn 设置成 3 也就是我们数据的长度,所以就第一列会合并。当然其他合并策略也可以自己写

  • 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭

web数据写出
@GetMapping("download")
public void download(HttpServletResponse response) throws IOException {response.setContentType("application/vnd.ms-excel");response.setCharacterEncoding("utf-8");// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系String fileName = URLEncoder.encode("数据写出", "UTF-8");response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");EasyExcel.write(response.getOutputStream(), DownloadData.class).sheet("模板").doWrite(data());
}
模板格式导出

如果需要横向填充只需要模板设置好就可以。

简单的Excel模板

public class FillData {private String name;private double number;// getting setting
}
实现模板填充
@Test
public void simpleFill() {String templateFileName = "simple.xlsx";// 方案1 根据对象填充String fileName = System.currentTimeMillis() + ".xlsx";// 这里 会填充到第一个sheet, 然后文件流会自动关闭FillData fillData = new FillData();fillData.setName("知春秋");fillData.setNumber(25);EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(fillData);// 方案2 根据Map填充fileName = System.currentTimeMillis() + ".xlsx";// 这里 会填充到第一个sheet, 然后文件流会自动关闭Map<String, Object> map = new HashMap<String, Object>();map.put("name", "知春秋");map.put("number", 25);EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(map);
}
  • 模板注意 用{} 来表示你要用的变量 如果本来就有"{“,”}" 特殊字符 用"{“,”}"代替
复杂的填充

使用List集合的方法批量写入数据,点表示该参数是集合

@Test
public void complexFill() {String templateFileName = "complex.xlsx";String fileName = System.currentTimeMillis() + ".xlsx";ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build();WriteSheet writeSheet = EasyExcel.writerSheet().build();// 如果数据量大 list不是最后一行 参照下一个FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();excelWriter.fill(data(), fillConfig, writeSheet);excelWriter.fill(data(), fillConfig, writeSheet);// 其他参数可以使用Map封装Map<String, Object> map = new HashMap<String, Object>();excelWriter.fill(map, writeSheet);excelWriter.finish();
}
  • 模板注意 用{} 来表示你要用的变量 如果本来就有"{“,”}" 特殊字符 用"{“,”}"代替

  • // {} 代表普通变量 {.} 代表是list的变量
    
  • // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行,然后下面的数据往后移动。默认 是false,会直接使用下一行,如果没有则创建。

  • // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了,所以慎用
    
  • // 简单的说 如果你的模板有list,且list不是最后一行,下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存
    

总结一个工具类


public class ExcelUtil {/*** 写出一个 excel 文件到本地* <br />* 将类型所有加了 @ExcelProperty 注解的属性全部写出** @param fileName  文件名 不要后缀* @param sheetName sheet名* @param data      写出的数据* @param clazz     要写出数据类的Class类型对象* @param <T>       写出的数据类型*/public static <T> void writeExcel(String fileName, String sheetName, List<T> data, Class<T> clazz) {writeExcel(null, fileName, sheetName, data, clazz);}/*** 按照指定的属性名进行写出 一个 excel** @param attrName  指定的属性名 必须与数据类型的属性名一致* @param fileName  文件名 不要后缀* @param sheetName sheet名* @param data      要写出的数据* @param clazz     要写出数据类的Class类型对象* @param <T>       要写出的数据类型*/public static <T> void writeExcel(Set<String> attrName, String fileName, String sheetName, List<T> data, Class<T> clazz) {fileName = StringUtils.isBlank(fileName) ? "学生管理系统" : fileName;sheetName = StringUtils.isBlank(sheetName) ? "sheet0" : sheetName;try(FileOutputStream fos = new FileOutputStream(fileName)) {write(fos,attrName,sheetName,data,clazz);} catch (Exception exception) {exception.printStackTrace();}}/*** 读取 指定格式的 excel文档** @param fileName 文件名* @param clazz    数据类型的class对象* @param <T>      数据类型* @return*/public static <T> List<T> readExcel(String fileName, Class<T> clazz) {return readExcel(fileName, clazz, null);}/*** 取 指定格式的 excel文档* 注意一旦传入自定义监听器,则返回的list为空,数据需要在自定义监听器里面获取** @param fileName     文件名* @param clazz        数据类型的class对象* @param readListener 自定义监听器* @param <T>          数据类型* @return*/public static <T> List<T> readExcel(String fileName, Class<T> clazz, ReadListener<T> readListener) {try(FileInputStream fis = new FileInputStream(fileName)) {return read(fis,clazz,readListener);} catch (Exception exception) {exception.printStackTrace();}}/*** 导出  一个 excel*         导出excel所有数据* @param response* @param fileName  件名 最好为英文,不要后缀名* @param sheetName sheet名* @param data      要写出的数据* @param clazz     要写出数据类的Class类型对象* @param <T>       要写出的数据类型*/public static <T> void export(HttpServletResponse response, String fileName, String sheetName, List<T> data, Class<T> clazz) {export(response, null, fileName, sheetName, data, clazz);}/*** 按照指定的属性名进行写出 一个 excel** @param response* @param attrName  指定的属性名 必须与数据类型的属性名一致* @param fileName  文件名 最好为英文,不要后缀名* @param sheetName sheet名* @param data      要写出的数据* @param clazz     要写出数据类的Class类型对象* @param <T>       要写出的数据类型*/public static <T> void export(HttpServletResponse response, Set<String> attrName, String fileName, String sheetName, List<T> data, Class<T> clazz) {fileName = StringUtils.isBlank(fileName) ? "student-system-manager" : fileName;sheetName = StringUtils.isBlank(sheetName) ? "sheet0" : sheetName;response.setContentType("application/vnd.ms-excel;charset=utf-8");response.setCharacterEncoding("utf-8");response.addHeader("Content-disposition", "attachment;filename=" + fileName + ExcelTypeEnum.XLSX.getValue());try(OutputStream os = response.getOutputStream()) {write(os,attrName,sheetName,data,clazz);} catch (IOException e) {e.printStackTrace();}}/*** 接收一个excel文件,并且进行解析*  注意一旦传入自定义监听器,则返回的list为空,数据需要在自定义监听器里面获取* @param multipartFile excel文件* @param clazz 数据类型的class对象* @param readListener 监听器* @param <T>* @return*/public static <T> List<T> importExcel(MultipartFile multipartFile,Class<T> clazz,ReadListener<T> readListener) {try(InputStream inputStream = multipartFile.getInputStream()) {return read(inputStream,clazz,readListener);} catch (IOException e) {e.printStackTrace();}}private static <T> void write(OutputStream os, Set<String> attrName, String sheetName, List<T> data, Class<T> clazz) {ExcelWriterBuilder write = EasyExcel.write(os, clazz);// 如果没有指定要写出那些属性数据,则写出全部if (!CollectionUtils.isEmpty(attrName)) {write.includeColumnFiledNames(attrName);}write.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet(sheetName).doWrite(data);}private static <T> List<T> read(InputStream in,Class<T> clazz, ReadListener<T> readListener) {List<T> list = new ArrayList<>();Optional<ReadListener> optional = Optional.ofNullable(readListener);EasyExcel.read(in, clazz, optional.orElse(new AnalysisEventListener<T>() {@Overridepublic void invoke(T data, AnalysisContext context) {list.add(data);}@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {System.out.println("解析完成")}})).sheet().doRead();return list;}
}

参考资料

  • https://github.com/alibaba/easyexcel/blob/master/docs/API.md 官方api
  • https://github.com/alibaba/easyexcel easyexcel github 地址
  • https://blog.csdn.net/sinat_32366329/article/details/103109058 easyexcel总结
  • https://alibaba-easyexcel.github.io/index.html 官方示例

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/413819.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DBA技术栈MongoDB:简介

1.1 什么是MongoDB&#xff1f; MongoDB是一个可扩展、开源、表结构自由、用C语言编写且面向文档的数据库&#xff0c;旨在为Web应用程序提供高性能、高可用性且易扩展的数据存储解决方案。 MongoDB是一个介于关系数据库和非关系数据库之间的产品&#xff0c;是非关系数据库当…

【前端设计】输入框

欢迎来到前端设计专栏&#xff0c;本专栏收藏了一些好看且实用的前端作品&#xff0c;使用简单的html、css语法打造创意有趣的作品&#xff0c;为网站加入更多高级创意的元素。 html <!DOCTYPE html> <html lang"en"> <head><meta charset&quo…

Redis--Zset使用场景举例(滑动窗口实现限流)

文章目录 前言什么是滑动窗口zset实现滑动窗口小结附录 前言 在Redis–Zset的语法和使用场景举例&#xff08;朋友圈点赞&#xff0c;排行榜&#xff09;一文中&#xff0c;提及了redis数据结构zset的指令语法和一些使用场景&#xff0c;今天我们使用zset来实现滑动窗口限流&a…

蓝天采集器,功能逆天的网站数据抓取神器,轻松助你成为采集达人,附带搭建配置文档

源码介绍 蓝天采集器是一款专为web服务器打造的数据采集神器。与市面上常见的桌面端采集工具&#xff08;如火车头等&#xff09;相比&#xff0c;蓝天采集器在易用性、上手成本和灵活性方面更胜一筹。它部署简便&#xff0c;无需复杂的设置&#xff0c;即可迅速融入您的web服…

python统计分析——样本均值的分布(上)

参考资料&#xff1a;用python动手学统计学 1、导入库 import numpy as np import pandas as pd import scipy as sp from scipy import statsfrom matplotlib import pyplot as plt import seaborn as sns 2、设置总体 本次研究总体是均值为4、标准差为0.8的正态总体。 po…

python222网站实战(SpringBoot+SpringSecurity+MybatisPlus+thymeleaf+layui)-热门帖子推荐显示实现

锋哥原创的SpringbootLayui python222网站实战&#xff1a; python222网站实战课程视频教程&#xff08;SpringBootPython爬虫实战&#xff09; ( 火爆连载更新中... )_哔哩哔哩_bilibilipython222网站实战课程视频教程&#xff08;SpringBootPython爬虫实战&#xff09; ( 火…

每日一题——LeetCode1266.访问所有点的最小时间

方法一 个人方法 找规律&#xff1a; 当前的点为current&#xff0c;下一个点为next&#xff0c;x为两点横坐标之间距离&#xff0c;y为两点竖坐标之间距离 1、当两点横坐标相同时&#xff0c;两点距离为y 2、当两点竖坐标相同时&#xff0c;两点距离为x 3、当两点x与y相同…

回归预测 | Matlab实现SSA-BP麻雀算法优化BP神经网络多变量回归预测

回归预测 | Matlab实现SSA-BP麻雀算法优化BP神经网络多变量回归预测 目录 回归预测 | Matlab实现SSA-BP麻雀算法优化BP神经网络多变量回归预测预测效果基本描述程序设计参考资料 预测效果 基本描述 1.Matlab实现SSA-BP麻雀算法优化BP神经网络多变量回归预测&#xff1b; 2.数据…

UML-实现图(组件图和部署图)

实现图是从系统的层次来描述的&#xff0c;描述硬件的组成和布局&#xff0c;描述软件系统划分和功能实现。 UML-实现图&#xff08;组件图和部署图&#xff09; 一、组件图1.组件图的元素&#xff08;1&#xff09;组件&#xff08;2&#xff09;接口&#xff08;3&#xff09…

C++后端笔记

C后端笔记 资源整理一、高级语言程序设计1.1 进制1.2 程序结构基本知识1.3 数据类型ASCII码命名规则变量间的赋值浮点型变量的作用字符变量常变量 const运算符 二、高级语言程序设计&#xff08;荣&#xff09; 资源整理 C后端开发学习路线及推荐学习时间 C基础知识大全 C那…

Flink TaskManager内存管理机制介绍与调优总结

内存模型 因为 TaskManager 是负责执行用户代码的角色&#xff0c;一般配置 TaskManager 内存的情况会比较多&#xff0c;所以本文当作重点讲解。根据实际需求为 TaskManager 配置内存将有助于减少 Flink 的资源占用&#xff0c;增强作业运行的稳定性。 TaskManager 内…

02 MyBatisPlus核心功能之基于Mapper接口CRUD+基于Service接口实现CRUD

项目结构&#xff1a; 1.1 Insert方法 // 插入一条记录 // T 就是要插入的实体对象 // 默认主键生成策略为雪花算法&#xff08;后面讲解&#xff09; //返回值是影响条数 int insert(T entity);1.2 Delete方法 // 根据 entity 条件&#xff0c;删除记录 int delete(Param(…