阿里巴巴EasyExcel学习笔记

为什么选择Alibaba EasyExcel

优势:

EasyExcel 重写了poi对07版Excel的解析,并且再大的excel也不会出现内存溢出

简单使用
一、写

1:引入依赖

 		<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>3.1.0</version>
        </dependency>

2:写一个文件地址工具类

import java.io.File;
import java.io.InputStream;

public class TestFileUtil {
    
    


    public static InputStream getResourcesFileInputStream(String fileName) {
    
    
        return Thread.currentThread().getContextClassLoader().getResourceAsStream("" + fileName);
    }

    public static String getPath() {
    
    
        return "C:/Users/Administrator/Desktop/测试/";
//        return TestFileUtil.class.getResource("/").getPath();
    }

    public static File createNewFile(String pathName) {
    
    
        File file = new File(getPath() + pathName);
        if (file.exists()) {
    
    
            file.delete();
        } else {
    
    
            if (!file.getParentFile().exists()) {
    
    
                file.getParentFile().mkdirs();
            }
        }
        return file;
    }

    public static File readFile(String pathName) {
    
    
        return new File(getPath() + pathName);
    }

    public static File readUserHomeFile(String pathName) {
    
    
        return new File(System.getProperty("user.home") + File.separator + pathName);
    }
}
简单写
 String fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";
        // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
        // 如果这里想使用03 则 传入excelType参数即可
        EasyExcel.write(fileName, DemoData.class)
                .sheet("模板")
                .doWrite(() -> {
    
    
                    // 分页查询数据
					//这里面可以放入查询数据的逻辑,通过return 将数据返过去,导出
                    return data();
                });

        

可以通过一个对象来设置所导出的表头名称

@Data
@NoArgsConstructor
@AllArgsConstructor
public class DemoData {
    
    
    @ExcelProperty("字符串标题")
    private String string;
    @ExcelProperty("日期标题")
    private Date date;
    @ExcelProperty("数字标题")
    private Double doubleData;
    /**
     * 忽略这个字段
     */
    @ExcelIgnore
    private String ignore;
}

@ExcelProperty 可以设置所导出字段的表头名称

对导出数据进行格式化

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

    private List<DemoData> data() {
    
    
        List<DemoData> list = ListUtils.newArrayList();
        for (int i = 0; i < 10; i++) {
    
    
            DemoData data = new DemoData();
            data.setString("字符串" + i);
            data.setDate(new Date());
            data.setDoubleData(0.5600);
            list.add(data);
        }
        return list;
    }
@Data
@NoArgsConstructor
@AllArgsConstructor
@ColumnWidth(40)//列宽
public class ConverterData {
    
    

 
    /**
     * 我想写到excel 用年月日的格式
     */
    @DateTimeFormat("yyyy-mm-dd HH:MM:ss")
    @ExcelProperty("日期标题")
    private Date date;
    /**
     * 我想写到excel 用百分比表示
     */
    @NumberFormat("0.00%")
    @ExcelProperty(value = "数字标题")
    private Double doubleData;
}
复杂表头导出 可以对某几个字段进行一个总标题合并

效果图如下
复杂标题导出

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

    private List<DemoData> data() {
    
    
        List<DemoData> list = ListUtils.newArrayList();
        for (int i = 0; i < 10; i++) {
    
    
            DemoData data = new DemoData();
            data.setString("字符串" + i);
            data.setDate(new Date());
            data.setDoubleData(0.56);
            list.add(data);
        }
        return list;
    }
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ComplexHeadData {
    
    

    @ExcelProperty({
    
    "主标题", "字符串标题"})
    private String string;

    @ExcelProperty({
    
    "主标题", "日期标题"})
    private Date date;

    @ExcelProperty({
    
    "主标题", "数字标题"})
    private Double doubleData;

    @ExcelProperty("测试")
    private String test;

}
合并单元格导出-通过对象设定合并单元格规则
@Test
    public void mergeWrite() {
    
    
        // 方法1 注解
        String fileName = TestFileUtil.getPath() + "合并单元格" + ".xlsx";
        // 在DemoStyleData里面加上ContentLoopMerge注解
        // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
        EasyExcel.write(fileName, DemoMergeData.class).sheet("模板").doWrite(data());
    }
@Data
@AllArgsConstructor
@NoArgsConstructor
public class DemoMergeData {
    
    

    // 这一列 每隔2行 合并单元格
    @ContentLoopMerge(eachRow = 2)
    @ExcelProperty("字符串标题")
    private String string;
    @ExcelProperty("日期标题")
    private Date date;
    @ExcelProperty("数字标题")
    private Double doubleData;
}   

效果如下:
在这里插入图片描述

合并单元格导出-第二种方式

        // 方法2 自定义合并单元格策略
        fileName = TestFileUtil.getPath() + "自定义合并单元格"  + ".xlsx";
        // 每隔2行会合并 把eachColumn 设置成 3 也就是我们数据的长度,所以就第一列会合并。当然其他合并策略也可以自己写
        LoopMergeStrategy loopMergeStrategy = new LoopMergeStrategy(3, 0);
        // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
        EasyExcel.write(fileName, DemoData.class).registerWriteHandler(loopMergeStrategy).sheet("模板").doWrite(data());

效果如下:
在这里插入图片描述

导出到多个sheet
   @Test
    public void repeatedWrite() {
    
    
        // 方法3 如果写到不同的sheet 不同的对象
            String   fileName = TestFileUtil.getPath() + "多sheet"  + ".xlsx";
        // 这里 指定文件
        try (ExcelWriter excelWriter = EasyExcel.write(fileName).build()) {
    
    
            WriteSheet writeSheet = EasyExcel.writerSheet(0, "测试一").head(DemoData.class).build();
            List<DemoData> data = data();
            excelWriter.write(data, writeSheet);
            WriteSheet writeSheet1 = EasyExcel.writerSheet(1, "测试二").head(DemoData.class).build();
            List<DemoData> data1 = data();
            excelWriter.write(data1, writeSheet1);
            WriteSheet writeSheet2 = EasyExcel.writerSheet(2, "测试三").head(DemoData.class).build();
            List<DemoData> data2 = data();
            excelWriter.write(data2, writeSheet2);
            WriteSheet writeSheet3 = EasyExcel.writerSheet(3, "测试四").head(DemoData.class).build();
            List<DemoData> data3 = data();
            excelWriter.write(data3, writeSheet3);

        }

    }

    private List<DemoData> data() {
    
    
        List<DemoData> list = ListUtils.newArrayList();
        for (int i = 0; i < 1000; i++) {
    
    
            DemoData data = new DemoData();
            data.setString("字符串" + i);
            data.setDate(new Date());
            data.setDoubleData(0.56);
            list.add(data);
        }
        return list;
    }

二、读操作

普通的读-可以读指定的sheet
 @Test
  public void simpleRead() {
    
    
      // 写法1:JDK8+ ,不用额外写一个DemoDataListener
      // since: 3.0.0-beta1
      String fileName = TestFileUtil.getPath() + File.separator + "多sheet.xlsx";
      // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
      // 这里每次会读取100条数据 然后返回过来 直接调用使用数据就行
      EasyExcel.read(fileName, DemoData.class, new PageReadListener<DemoData>(dataList -> {
    
    
          for (DemoData demoData : dataList) {
    
    
              log.info("读取到一条数据{}", JSON.toJSONString(demoData));
          }
      })).sheet("测试").doRead();

  }
读取多个sheet
@Test
 public void repeatedRead() {
    
    
     String fileName = TestFileUtil.getPath() + File.separator + "多sheet.xlsx";
     // 读取全部sheet
     // 这里需要注意 DemoDataListener的doAfterAllAnalysed 会在每个sheet读取完毕后调用一次。然后所有sheet都会往同一个DemoDataListener里面写
     EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).doReadAll();

     // 读取部分sheet
     fileName = TestFileUtil.getPath() + File.separator + "多sheet.xlsx";

     // 写法1
     try (ExcelReader excelReader = EasyExcel.read(fileName).build()) {
    
    
         // 这里为了简单 所以注册了 同样的head 和Listener 自己使用功能必须不同的Listener
         ReadSheet readSheet1 =
                 EasyExcel.readSheet(0).head(DemoData.class).registerReadListener(new DemoDataListener()).build();
         ReadSheet readSheet2 =
                 EasyExcel.readSheet(1).head(DemoData.class).registerReadListener(new DemoDataListener()).build();
         ReadSheet readSheet3 =
                 EasyExcel.readSheet(2).head(DemoData.class).registerReadListener(new DemoDataListener()).build();
         ReadSheet readSheet4 =
                 EasyExcel.readSheet(3).head(DemoData.class).registerReadListener(new DemoDataListener()).build();
         // 这里注意 一定要把sheet1 sheet2 一起传进去,不然有个问题就是03版的excel 会读取多次,浪费性能
         excelReader.read(readSheet1, readSheet2,readSheet3,readSheet4);
     }
 }  
不创建对象的读
  @Test
  public void noModelRead() {
    
    
      String fileName = TestFileUtil.getPath() +  "测试.xlsx";
      // 这里 只要,然后读取第一个sheet 同步读取会自动finish
      EasyExcel.read(fileName, new NoModelDataListener()).sheet().doRead();
      Set<Channel> channelSet = ExcelUtil.channelSet;
      int count = 0;
      for (Channel channel : channelSet) {
    
    
          count++;
          System.out.println("第"+count+"个"+channel);
      }
  } 
@Slf4j
public class NoModelDataListener extends AnalysisEventListener<Map<Integer, String>> {
    
    
  /**
   * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
   */
  private static final int BATCH_COUNT = 5;
  private List<Map<Integer, String>> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

  @Override
  public void invoke(Map<Integer, String> data, AnalysisContext context) {
    
    
//        log.info("解析到一条数据:{}", JSON.toJSONString(data));
      cachedDataList.add(data);
      if (cachedDataList.size() >= BATCH_COUNT) {
    
    
          saveData();
          cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
      }
  }

  @Override
  public void doAfterAllAnalysed(AnalysisContext context) {
    
    
      saveData();
      log.info("所有数据解析完成!");
  }

  /**
   * 加上存储数据库
   */
  private void saveData() {
    
    
      for (Map<Integer, String> map : cachedDataList) {
    
    
          String pid = "0";
          int size = map.size();
          for (int i = 0; i < size; i++) {
    
    
              String value = map.get(i);
              if (value == null) {
    
    
                  continue;
              }
              int index = value.indexOf(",");
              Channel channel = new Channel();
              if (index < 0) {
    
    
                  channel.setPid(pid);
                  channel.setId(value);
                  channel.setName(value);
                  pid = value;
              } else {
    
    
                  String name = value.substring(0, index);
                  channel.setPid(pid);
                  String id = value.substring(index + 1);
                  channel.setId(id);
                  channel.setName(name);
                  pid = id;
              }
              ExcelUtil.channelSet.add(channel);
          }
      }
  }

猜你喜欢

转载自blog.csdn.net/qq_44794782/article/details/125873264