写点什么

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

发布于: 刚刚
🏆【Alibaba工具型技术系列】「EasyExcel技术专题」实战技术针对于项目中常用的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 文件
@Testpublic 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")@ResponseBodypublic 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 */ @Override public void invoke(DemoData data, AnalysisContext context) { System.out.println("解析到一条数据:{}", JSON.toJSONString(data)); list.add(data); }
/** * 所有数据解析完成了 都会来调用 * * @param context */ @Override public void doAfterAllAnalysed(AnalysisContext context) { System.out.println(JSON.toJSONString(list)); }}
复制代码
代码
@Testpublic 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
@Testpublic void repeatedRead() {    String fileName = "demo.xlsx";    // 读取全部sheet    // 这里需要注意 DemoDataListener的doAfterAllAnalysed 会在每个sheet读取完毕后调用一次。然后所有sheet都会往同一个DemoDataListener里面写    EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).doReadAll();    // 读取部分sheet    fileName = "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> {      @Override    public Class supportJavaTypeKey() {        return String.class;    }
@Override public CellDataTypeEnum supportExcelTypeKey() { return CellDataTypeEnum.STRING; }
/** * 这里读的时候会调用 * * @param cellData * NotNull * @param contentProperty * Nullable * @param globalConfiguration * NotNull * @return */ @Override public String convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { return "自定义:" + cellData.getStringValue(); }
/** * 这里是写的时候会调用 不用管 * * @param value * NotNull * @param contentProperty * Nullable * @param globalConfiguration * NotNull * @return */ @Override public CellData convertToExcelData(String value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { return new CellData(value); }}
复制代码

多行头

@Testpublic 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 */@Overridepublic void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {    LOGGER.info("解析到一条头数据:{}", JSON.toJSONString(headMap));}
复制代码

异常处理

覆盖监听器 onException 方法


/*** 监听器实现这个方法就可以在读取数据的时候获取到异常信息*/@Overridepublic 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());    }}
复制代码

导出指定的列

@Testpublic 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";    // 根据用户传入字段 假设我们只要导出 date    Set<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;}
@Testpublic 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) // 列宽


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


@Testvoid contextLoads() {    EasyExcel.write("自适应.xlsx", Student.class)        .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())        .sheet()        .doWrite(getData());}
复制代码

动态表头

@Testpublic 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;}
复制代码

合并单元格

 @Test public 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}
复制代码
实现模板填充
@Testpublic 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 集合的方法批量写入数据,点表示该参数是集合


@Testpublic 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();}
复制代码


  • 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"{","}"代替

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

总结一个工具类


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>() {
@Override public void invoke(T data, AnalysisContext context) { list.add(data); }
@Override public 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 官方示例

发布于: 刚刚阅读数: 3
用户头像

🏆 2021年InfoQ写作平台-签约作者 🏆 2020.03.25 加入

【个人简介】酷爱计算机技术、醉心开发编程、喜爱健身运动、热衷悬疑推理的”极客狂人“ 【技术格言】任何足够先进的技术都与魔法无异 【技术范畴】Java领域和Spring生态、MySQL专项、APM专题及微服务/分布式体系等

评论

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