POI和EasyExcel的使用
POI是什么
Apache POI是Apache软件基金会的开放源码函式库,POI提供API给Java程序对Microsoft Office格式档案读和写的功能.可以用于把数据导出为excel表格或者是把excel表的数据录入到数据库中.
依赖
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
excel对象
-
工作簿(文件)
-
工作表(sheet)
-
行
-
列
表写入
@Test
public void test1() throws IOException {
//HSSFWorkbook为03版excel表,07版使用XSSFWorkbook
Workbook workbook = new HSSFWorkbook();
Sheet sheet = workbook.createSheet("my_sheet");
//第一行
Row row_1 = sheet.createRow(0);
//第一行的第一个单元格
Cell cell_1_1 = row_1.createCell(0);
cell_1_1.setCellValue("姓名");
Cell cell_1_2 = row_1.createCell(1);
cell_1_2.setCellValue("野原新之助");
Row row_2 = sheet.createRow(1);
Cell cell_2_1 = row_2.createCell(0);
cell_2_1.setCellValue("年龄");
Cell cell_2_2 = row_2.createCell(1);
cell_2_2.setCellValue(5);
//03版excel表结尾为xls,07版excel表结尾为xlsx
FileOutputStream stream = new FileOutputStream(PATH + "test1.xls");
workbook.write(stream);
stream.close();
workbook.close();
}
数据批量导入:
- HSSF
- 最多只能处理65536行数据
- 过程中写入缓存,不操作磁盘,最后一次性写入磁盘,速度快
- XSSF
- 速度慢,耗内存,也会发生内存溢出
- 可以写较大的数据量
- SXSSF
- 加速版的XSSF
- 过程中会产生临时文件,需要清理临时文件
- 默认100条记录保存在内存中,超过写入
- 如果要自定义内存中的数据量:new SXSSFWorkBook(数量)
- 清除临时文件:==((SXSSFWorkBook)workBook).dispose()
表读取
@Test
public void test1() throws IOException {
FileInputStream stream = new FileInputStream(PATH + "test1.xls");
//使用对应的workbook打开对应版本的excel表
Workbook workbook = new HSSFWorkbook(stream);
Sheet sheet = workbook.getSheetAt(0);
for (int rowNum = 0; rowNum < 2; rowNum++) {
Row row = sheet.getRow(rowNum);
for (int cellNum = 0; cellNum < 2; cellNum++) {
Cell cell = row.getCell(cellNum);
System.out.println(cell);
}
}
stream.close();
workbook.close();
}
EasyExcel是什么
Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。easyexcel重写了poi对07版Excel的解析,能够原本一个3M的excel用POI sax依然需要100M左右内存降低到几M,并且再大的excel不会出现内存溢出,03版依赖POI的sax模式。在上层做了模型转换的封装,让使用者更加简单方便
依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.6</version>
</dependency>
简单使用
实体类
@Data
public class User {
@ExcelProperty("用户名")
private String username;
@ExcelIgnore
private String password;
@ExcelProperty("生日")
private Date birthday;
@ExcelProperty("余额")
private Double money;
}
表写入
private List<User> user(){
List<User> list = new ArrayList<User>();
for (int i = 0; i < 10; i++) {
User user = new User();
user.setUsername("用户" + i);
user.setPassword(String.valueOf(Math.random()));
user.setBirthday(new Date());
user.setMoney(3.14);
list.add(user);
}
return list;
}
@Test
public void simpleWrite() {
// 写法1
String fileName = PATH + "user1.xlsx";
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
// 如果这里想使用03 则 传入excelType参数即可
EasyExcel.write(fileName, User.class).sheet("自定义sheet名").doWrite(user());
// 写法2
fileName = PATH + "user2.xlsx";
// 这里 需要指定写用哪个class去写
ExcelWriter excelWriter = null;
try {
excelWriter = EasyExcel.write(fileName, User.class).build();
WriteSheet writeSheet = EasyExcel.writerSheet("用户表").build();
excelWriter.write(user(), writeSheet);
} finally {
// 千万别忘记finish 会帮忙关闭流
if (excelWriter != null) {
excelWriter.finish();
}
}
}
持久层
public class DemoDAO {
public void save(List<User> list) {
System.out.println("list->保存到数据库!");
}
}
监听器
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
public class DemoDataListener extends AnalysisEventListener<User> {
private static final int BATCH_COUNT = 5;
List<User> list = new ArrayList<User>();
private DemoDAO demoDAO;
public DemoDataListener() {
// 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
demoDAO = new DemoDAO();
}
public DemoDataListener(DemoDAO demoDAO) {
this.demoDAO = demoDAO;
}
@Override
public void invoke(User data, AnalysisContext context) {
System.out.println("解析到一条数据:" + JSON.toJSONString(data));
list.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (list.size() >= BATCH_COUNT) {
saveData();
// 存储完成清理 list
list.clear();
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
saveData();
System.out.println("所有数据解析完成!");
}
private void saveData() {
System.out.println(list.size() + "条数据,开始存储数据库!");
demoDAO.save(list);
System.out.println("存储数据库成功!");
}
}
表读取
@Test
public void simpleRead() {
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
// 写法1:
String fileName = PATH + "user1.xlsx";
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
EasyExcel.read(fileName, User.class, new DemoDataListener()).sheet().doRead();
// 写法2:
fileName = PATH + "user2.xlsx";
ExcelReader excelReader = null;
try {
excelReader = EasyExcel.read(fileName, User.class, new DemoDataListener()).build();
ReadSheet readSheet = EasyExcel.readSheet(0).build();
excelReader.read(readSheet);
} finally {
if (excelReader != null) {
// 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的
excelReader.finish();
}
}
}