赞
踩
Java解析、生成Excel比较有名的框架有Apache poi、jxl等,使用者可自行斟酌。
Apache poi、jxl也能解析Excel,但他们都存在一个问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。
easyexcel重写了poi对07版Excel的解析,一个3M的excel用POI sax解析依然需要100M左右内存,改用easyexcel可以降低到几M,并且再大的excel也不会出现内存溢出;03版依赖POI的sax模式,在上层做了模型转换的封装,让使用者更加简单方便。
EasyExcel 可以映射excel和实体类,让代码变的更加简洁,读写更方便。
<!-- spring-boot 使用自己项目的版本就好 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- alibaba 相关依赖 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>3.3.3</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> </dependency> <!-- lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.24</version> </dependency>
姓名 | 证件号 | 证件类型 |
---|---|---|
张三 | 500000000000000000x | 户口簿 |
李四 | 5111111111111111111 | 身份证 |
王不二 | 5222222222222222222 | 身份证 |
赵五 | 5252513xdg | 护照 |
import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.format.DateTimeFormat; import com.alibaba.excel.annotation.format.NumberFormat; import lombok.Data; import lombok.EqualsAndHashCode; /** * @author lqf * @date 2024/1/11 11:00 */ @Data @EqualsAndHashCode public class AccountCenterByExcelVO { /** * 姓名 */ // 可以使用指定 表格下标或者 表头名字 的方式来对应数据。视情况选择,优弊如下: // 使用表下标,会让制表变得不灵活,使用表名会造成 名字重复,只有一个字段读取到数据 // @ExcelProperty(index = 1) @ExcelProperty("姓名") private String name; /** * 证件号 */ @ExcelProperty("证件号") private String IdNumber; /** * 证件类型 */ @ExcelProperty("证件类型") private String IdType; /** * 这里用string 去接日期才能格式化。我想接收年月日格式 */ @ExcelProperty("日期") @DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒") private String date; /** * 想接收百分比的数字 */ @ExcelProperty("余额") @NumberFormat("#.##%") private String doubleData; }
import com.alibaba.excel.read.listener.ReadListener; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.util.ListUtils; import com.alibaba.fastjson.JSON; import com.insupro.flexibleServerJ.dto.resp.vo.AccountCenterByExcelVO; import lombok.extern.slf4j.Slf4j; import java.util.List; /** * 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去 * @author lqf */ @Slf4j public class ACByExcelListener implements ReadListener<AccountCenterByExcelVO> { /** * 每隔 N 条 进行一次数据处理,根据实际使用情况处理,然后清理list ,方便内存回收 */ private static final int BATCH_COUNT = 100; /** * 缓存的数据 */ private List<AccountCenterByExcelVO> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); /** * 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。 * 用来存储或者处理数据 */ private AccountCenterByExcelVO excelVo; /** * 每次创建Listener的时候需要把spring管理的类传进来 * * @param excelVo */ public ACByExcelListener(AccountCenterByExcelVO excelVo) { this.excelVo = excelVo; } /** * 这个每一条数据解析都会来调用 * * @param data * @param context */ @Override public void invoke(AccountCenterByExcelVO data, AnalysisContext context) { cachedDataList.add(data); // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM if (cachedDataList.size() >= BATCH_COUNT) { saveData(); // 存储完成清理 list cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); } } /** * 所有数据解析完成了 都会来调用 * * @param context */ @Override public void doAfterAllAnalysed(AnalysisContext context) { // 这里也要保存数据,确保最后遗留的数据也存储到数据库 saveData(); log.info("所有数据解析完成!"); } /** * 加上存储数据库 */ public void saveData() { log.info("{}条数据,开始处理数据!", cachedDataList.size()); log.info("解析结果:"+JSON.toJSONString(cachedDataList)); // 调用业务逻辑服务处理数据,实例是假装有业务逻辑 log.info(excelVo.toString()); log.info("数据处理成功!"); } }
只是示例代码,业务逻辑没有写入server 层,使用的时候自行将业务代码归类
import com.alibaba.excel.EasyExcel; import com.alibaba.excel.read.listener.PageReadListener; import com.alibaba.fastjson.JSON; import com.insupro.flexibleServerJ.dto.resp.vo.AccountCenterByExcelVO; import com.insupro.flexibleServerJ.utils.ACByExcelListener; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import java.util.ArrayList; import java.util.List; /** * @author lqf * @date 2024/1/11 13:46 */ @Slf4j @RestController @RequestMapping("api/easyExcel") public class EasyExcelController { @RequestMapping("upExcel") public String upExcel(MultipartFile multipartFile){ try { List<AccountCenterByExcelVO> acvoList = new ArrayList<>(); // 第一种 使用 EasyExcel 自带的 PageReadListener,可以将数据读取后拿到读取的所有数据 // 这里默认每次会读取100条数据 然后返回过来 直接调用使用数据就行 // 具体需要返回多少行可以在`PageReadListener`的构造函数设置 EasyExcel.read(multipartFile.getInputStream(), AccountCenterByExcelVO.class, new PageReadListener<AccountCenterByExcelVO>(dateList ->{ // 这里默认每次会读取100条数据,然后进入下一次读取 log.info(JSON.toJSONString(dateList)); // 保存每一次的数据读取即可 acvoList.addAll(dateList); })) .sheet() .doRead(); System.out.println(acvoList); // 第二种,使用自定义监听器, 数据集处理需要再监听器中定义 EasyExcel.read(multipartFile.getInputStream(), AccountCenterByExcelVO.class, new ACByExcelListener(new AccountCenterByExcelVO())) .sheet() // 不添加属性,默认头部是1行 .headRowNumber(1) .doRead(); }catch (Exception e){ e.printStackTrace(); } return "完成"; } }
import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.format.DateTimeFormat; import com.alibaba.excel.annotation.format.NumberFormat; import lombok.Data; import lombok.EqualsAndHashCode; import java.util.Date; /** * @author lqf * @date 2024/1/11 11:00 */ @Data @EqualsAndHashCode public class AccountWriteForExcelVO { /** * 姓名 * */ // 可以使用指定 表格下标或者 表头名字 的方式来对应数据。视情况选择,优弊如下: // 使用表下标,会让制表变得不灵活,使用表名会造成 名字重复,只有一个字段读取到数据 // @ExcelProperty(index = 1) @ExcelProperty("姓名") private String name; /** * 证件号 */ @ExcelProperty("证件号") private String IdNumber; /** * 证件类型 */ @ExcelProperty("证件类型") private String IdType; /** * 这里用string 去接日期才能格式化。我想接收年月日格式 */ @ExcelProperty("日期") @DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒") private Date date; /** * 想接收百分比的数字 */ @ExcelProperty("余额") @NumberFormat("#.##%") private Double doubleData; }
import com.alibaba.excel.EasyExcel; import com.alibaba.excel.ExcelWriter; import com.alibaba.excel.util.ListUtils; import com.alibaba.excel.write.metadata.WriteSheet; import vo.AccountWriteForExcelVO; import java.util.Date; import java.util.List; /** * @author lqf * @date 2024/1/11 11:00 */ public class WriteExcleDemo { public static void main(String[] args) { // 使用自己指定输出的文件路径 String fileName = "D:/workSpace/" + "simpleWrite" + System.currentTimeMillis() + ".xlsx"; System.out.println(fileName); // 注意 数据量大参照 重复多次写入 // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 // 如果这里想使用03 则 传入excelType参数即可 EasyExcel.write(fileName, AccountWriteForExcelVO.class) .sheet("模板") .doWrite(() -> { // 分页查询数据 return data(); }); // 写法2 // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 // 如果这里想使用03 则 传入excelType参数即可 EasyExcel.write(fileName, AccountWriteForExcelVO.class).sheet("模板").doWrite(data()); // 写法3 // 这里 需要指定写用哪个class去写 try (ExcelWriter excelWriter = EasyExcel.write(fileName, AccountWriteForExcelVO.class).build()) { WriteSheet writeSheet = EasyExcel.writerSheet("模板").build(); excelWriter.write(data(), writeSheet); } } private static List<AccountWriteForExcelVO> data() { List<AccountWriteForExcelVO> list = ListUtils.newArrayList(); for (int i = 0; i < 10; i++) { AccountWriteForExcelVO data = new AccountWriteForExcelVO(); data.setName("字符串" + i); data.setDate(new Date()); data.setDoubleData(0.56); list.add(data); } return list; } }
更多使用方法,见官方文档
https://easyexcel.opensource.alibaba.com/docs/current/
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。