easyexcel和poi对比_POI和easyExcel

这篇博客对比了Apache POI和EasyExcel在处理Excel数据时的不同。Apache POI支持03和07版本的Excel,适用于Java读写MS Office文件,但处理大文件时可能会遇到性能问题。而EasyExcel在处理大数据量时更优,且使用简单。博客通过代码示例展示了如何使用这两个库进行数据的导入导出,并提供了读写操作的性能比较。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

常用场景

将用户信息导出为excel表格

将excel的信息导入到数据库,减轻大量网站录入量 开发经常使用到excel导入导出

Apache POI官网:

Apache POI项目的任务是创建和维护Java API,以基于Office Open

XML标准(OOXML)和Microsoft的OLE 2复合文档格式(OLE2)来处理各种文件格式。简而言之,您可以使用Java读写MS

Excel文件。此外,您可以使用Java读写MS Word和MS PowerPoint文件。Apache POI是您的Java Excel解决方案(适用于Excel 97-2008)。

easyexcel官网:

POI使用

创建普通的maven项目

导入依赖

org.apache.poi

poi

3.9

org.apache.poi

poi-ooxml

3.9

joda-time

joda-time

2.10.5

junit

junit

4.12

xls03和xls07的区别 : 数据量的大小 03的只有65535行

03版本的:

创建测试类

static String path="H:\\优快云\\POI\\src\\main\\java\\com\\dj";

@Test

public void testWrite03() throws IOException {

//1. 创建工作簿

Workbook workbook=new HSSFWorkbook();

//2. 创建工作表 参数:表的名称

Sheet sheet=workbook.createSheet("joker_djの表");

//3. 创建一个行

Row row1=sheet.createRow(0);

//4. 创建一个单元格

Cell cell1 = row1.createCell(0);//第一行的第一列

cell1.setCellValue("姓名");//设置值

Cell cell2 = row1.createCell(1);//第一行的第二列

cell2.setCellValue("joker_dj");//设置值

//第二行

Row row2=sheet.createRow(1);

//4. 创建一个单元格

Cell cell21 = row2.createCell(0);//第二行的第一列

cell21.setCellValue("统计时间");//设置值

Cell cell22 = row2.createCell(1);//第二行的第二列

String time = new DateTime().toString("yyyy-MM-dd HH:mm:ss");

cell22.setCellValue(time);//设置值

//生成一张表

FileOutputStream fileOutputStream = new FileOutputStream(path + "/统计表03.xls");

workbook.write(fileOutputStream);//输出文件内容

//关闭流

fileOutputStream.close();

System.out.println("文件输出完毕");

}

输出效果

07版本的

测试

@Test

public void testWrite07() throws IOException {

//1. 创建工作簿

Workbook workbook=new XSSFWorkbook();

//2. 创建工作表 参数:表的名称

Sheet sheet=workbook.createSheet("joker_djの表");

//3. 创建一个行

Row row1=sheet.createRow(0);

//4. 创建一个单元格

Cell cell1 = row1.createCell(0);//第一行的第一列

cell1.setCellValue("姓名");//设置值

Cell cell2 = row1.createCell(1);//第一行的第二列

cell2.setCellValue("joker_dj");//设置值

//第二行

Row row2=sheet.createRow(1);

//4. 创建一个单元格

Cell cell21 = row2.createCell(0);//第二行的第一列

cell21.setCellValue("统计时间");//设置值

Cell cell22 = row2.createCell(1);//第二行的第二列

String time = new DateTime().toString("yyyy-MM-dd HH:mm:ss");

cell22.setCellValue(time);//设置值

//生成一张表

FileOutputStream fileOutputStream = new FileOutputStream(path + "/统计表07.xlsx");

workbook.write(fileOutputStream);//输出文件内容

//关闭流

fileOutputStream.close();

System.out.println("文件输出完毕");

}

注意文件后缀

数据批量导入

大文件写入HSSF

缺点:最多只能处理65536行否则会抛出异常

优点:过程中写入缓存,最后一次写入磁盘,速度快

大文件写入XSSF

缺点:写数据速度非常慢,消耗内存,也会内存溢出 入100万条

优点:可以写较大的数据量

03版本批量插入

编写批量写入 65536行数据 超出就会报错

@Test

public void testbranch03() throws IOException {

//开始时间

long start = System.currentTimeMillis();

//创建一个工作簿

Workbook workbook=new HSSFWorkbook();

//创建一个工作表

Sheet sheet = workbook.createSheet();

for (int rownum = 0; rownum < 65536; rownum++) {

Row row1 = sheet.createRow(rownum);

for (int celnum = 0; celnum < 10; celnum++) {

Cell cell = row1.createCell(celnum);//第一行第一列

cell.setCellValue(celnum);

}

}

System.out.println("end");

FileOutputStream fileOutputStream = new FileOutputStream(path + "/brach统计表03.xls");

workbook.write(fileOutputStream);

fileOutputStream.close();

//结束时间时间

long end = System.currentTimeMillis();

double begin=(end-start)/1000;

System.out.println("执行时间"+begin);

}

07版本批量插入

@Test

public void testbranch04() throws IOException {

//开始时间

long start = System.currentTimeMillis();

//创建一个工作簿

Workbook workbook=new XSSFWorkbook();

//创建一个工作表

Sheet sheet = workbook.createSheet();

for (int rownum = 0; rownum < 65537; rownum++) {

Row row1 = sheet.createRow(rownum);

for (int celnum = 0; celnum < 10; celnum++) {

Cell cell = row1.createCell(celnum);//第一行第一列

cell.setCellValue(celnum);

}

}

System.out.println("end");

FileOutputStream fileOutputStream = new FileOutputStream(path + "/brach统计表04.xlsx");

workbook.write(fileOutputStream);

fileOutputStream.close();

//结束时间时间

long end = System.currentTimeMillis();

double begin=(end-start)/1000;

System.out.println("执行时间"+begin);

}

耗时较长,但插入的数据可以很多

升级版07 加速运行时间 但会产生临时文件

我们可以手动清除临时文件

POI读取数据

注意点 :数据类型

String path="H:\\优快云\\POI\\";

@Test

public void test01() throws IOException {

FileInputStream fileInputStream = new FileInputStream("H:\\优快云\\POI\\统计表03.xls");

Workbook workbook = new HSSFWorkbook(fileInputStream);

Sheet sheet = workbook.getSheetAt(0);//获取第1个工作表

Row row = sheet.getRow(0);//得到一行

Cell cell = row.getCell(0);//获取第一列

System.out.println(cell.getStringCellValue());

fileInputStream.close();

}

日期类型:

getDateCellValue() 日期

getStringCellValue() 字符串

getBooleanCellValue()布尔类型

07版本的读取一样

数据类型

@Test

public void test03() throws IOException {

FileInputStream fileInputStream = new FileInputStream("H:\\优快云\\POI\\java35班.xlsx");

Workbook workbook = new XSSFWorkbook(fileInputStream);

Sheet sheet = workbook.getSheetAt(0);

Row row = sheet.getRow(0);//获取标题

if(row!=null){//如果第一行不为空

int cellncount = row.getPhysicalNumberOfCells();//获取第一行的第一列数据

for (int cellnum = 0; cellnum < cellncount; cellnum++) {

Cell cell = row.getCell(cellnum);

if(cell!=null){

int cellType = cell.getCellType();

String stringCellValue = cell.getStringCellValue();

System.out.print(stringCellValue+"|");

}

}

System.out.println();

}

//获取表中的内容

int rowcount = sheet.getPhysicalNumberOfRows();

for (int rownum = 1; rownum < rowcount; rownum++) {//获取行的数据

Row rowData = sheet.getRow(rownum);

if(rowData!=null){

//读取列

int cellcount = row.getPhysicalNumberOfCells();

for (int celnum = 0; celnum < cellcount; celnum++) {

// System.out.print("【"+rownum+1+"】"+(celnum+1));

Cell cell = rowData.getCell(celnum);

/*判断数据类型*/

if(cell!=null){

int cellType = cell.getCellType();

String cellValue="";

switch (cellType){

case HSSFCell.CELL_TYPE_STRING://string类型

//System.out.println("【String】");

cellValue=cell.getStringCellValue();

break;

case HSSFCell.CELL_TYPE_BOOLEAN://布尔类型

// System.out.println("【boolean】");

cellValue=String.valueOf(cell.getBooleanCellValue());

break;

case HSSFCell.CELL_TYPE_NUMERIC: //数字和日期类型

System.out.println("【NUMBER】");

if(HSSFDateUtil.isCellDateFormatted(cell)){

// System.out.println("【日期】");

Date date=cell.getDateCellValue();

cellValue=new DateTime(date).toString("yyy-MM-dd");

}else{

// System.out.println("【转换为字符串输出】");

cell.setCellType(HSSFCell.CELL_TYPE_STRING);

cellValue=cell.toString();

}

case HSSFCell.CELL_TYPE_ERROR://布尔类型

// System.out.println("【数据类型错误】");

cellValue=String.valueOf(cell.getErrorCellValue());

break;

}

System.out.print(cellValue+"\n");

}

}

}

}

fileInputStream.close();

}

计算公式

//计算公式

@Test

public void test04() throws IOException {

FileInputStream fileInputStream = new FileInputStream("H:\\优快云\\POI\\统计表07.xlsx");

Workbook workbook = new XSSFWorkbook(fileInputStream);

Sheet sheet = workbook.getSheetAt(0);//获取第1个工作表

Row row = sheet.getRow(4);//得到一行

Cell cell = row.getCell(0);

//拿到计算公式

FormulaEvaluator formulaEvaluator = new XSSFFormulaEvaluator((XSSFWorkbook) workbook);

//拿到单元格内容

int cellType = cell.getCellType();

switch (cellType){

case Cell.CELL_TYPE_FORMULA://公式

String cellFormula = cell.getCellFormula();

System.out.println(cellFormula);//输出公式

//计算

CellValue evaluate = formulaEvaluator.evaluate(cell);

String cellValue = evaluate.formatAsString();

System.out.println(cellValue);

}

fileInputStream.close();

}

easyexcel操作

写入数据

导入依赖

joda-time

joda-time

2.10.5

junit

junit

4.12

com.alibaba

easyexcel

2.2.6

org.projectlombok

lombok

1.18.12

provided

编写实体类

@Data

public class demoData {

@ExcelProperty("字符串标题")

private String string;

@ExcelProperty("日期标题")

private Date date;

@ExcelProperty("数字标题")

private Double doubleData;

/**

* 忽略这个字段

*/

@ExcelIgnore

private String ignore;

}

效果

public class Testwrite {

private List data(){

List data=new ArrayList();

for (int i = 0; i < 10; i++) {

demoData demoData = new demoData();

demoData.setString("字符串"+i);

demoData.setDate(new Date());

demoData.setDoubleData(0.56);

data.add(demoData);

}

return data;

}

//引入easyexcel

/**

* 最简单的写

*

1. 创建excel对应的实体对象 参照{@link demoData}

*

2. 直接写即可

*/

@Test

public void simpleWrite() {

// 写法1

String fileName ="H:\\优快云\\easyexcel\\src\\main\\java\\test\\easyexcel.xlsx";

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

// 如果这里想使用03 则 传入excelType参数即可

EasyExcel.write(fileName, demoData.class).sheet("模板").doWrite(data());

}

}

读取数据

编写监听器

// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去

public class DemoDataListener extends AnalysisEventListener {

private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class);

/**

* 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收

*/

private static final int BATCH_COUNT = 5;

List list = new ArrayList();

/**

* 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。

*/

private DemoDAO demoDAO;

public DemoDataListener() {

// 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数

demoDAO = new DemoDAO();

}

/**

* 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来

*

* @param demoDAO

*/

public DemoDataListener(DemoDAO demoDAO) {

this.demoDAO = demoDAO;

}

/**

* 这个每一条数据解析都会来调用

*

* @param data

* one row value. Is is same as {@link AnalysisContext#readRowHolder()}

* @param context

*/

@Override

public void invoke(DemoData data, AnalysisContext context) {

System.out.println(JSON.toJSONString(data));

LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));

list.add(data);

// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM

if (list.size() >= BATCH_COUNT) {

saveData();

// 存储完成清理 list

list.clear();

}

}

/**

* 所有数据解析完成了 都会来调用

*

* @param context

*/

@Override

public void doAfterAllAnalysed(AnalysisContext context) {

// 这里也要保存数据,确保最后遗留的数据也存储到数据库

saveData();//保存数据库

LOGGER.info("所有数据解析完成!");

}

/**

* 加上存储数据库

*/

private void saveData() {

LOGGER.info("{}条数据,开始存储数据库!", list.size());

demoDAO.save(list);

LOGGER.info("存储数据库成功!");

}

}

编写dao层 可不写 用来持久化操作

/**

* 假设这个是你的DAO存储。当然还要这个类让spring管理,当然你不用需要存储,也不需要这个类。

**/

public class DemoDAO {

public void save(List list) {

// 如果是mybatis,尽量别直接调用多次insert,自己写一个mapper里面新增一个方法batchInsert,所有数据一次性插入

}

}

测试读取

public class TestRead {

/**

* 最简单的读

*

1. 创建excel对应的实体对象 参照{@link DemoData}

*

2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener}

*

3. 直接读即可

*/

@Test

public void simpleRead() {

// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去

// 写法1:

String fileName ="H:\\优快云\\easyexcel\\src\\main\\java\\test\\easyexcel.xlsx";

// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭

EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();

}

}

效果

<think>首先,用户的问题是:“我想了解EasyExcelPOI同时使用的理由 请问为什么需要同时使用EasyExcelPOI”。这是在询问为什么在项目中会同时使用EasyExcelApache POI。 回顾系统级指令: - 所有行内数学表达式必须使用$...$格式。 - 独立公式使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识。 - 回答结构清晰,帮助用户逐步解决问题。 - 参考站内引用:用户提供了引用[1]引用[2],涉及EasyExcelPOI版本冲突、优缺点对比。 引用内容: - [^1]: 解决EasyExcelPOI版本冲突问题。排除EasyExcel自带的POI依赖。 - [^2]: EasyExcelPOI优缺点对比EasyExcel基于SAX,适合大文件,避免内存溢出;POI功能齐全,但内存占用大。 用户的问题焦点是:为什么需要同时使用两者。 我的回答需要: 1. 结构清晰:逐步解释原因。 2. 真实可靠:基于提供的引用一般知识。 3. 使用中文。 4. 在回答末尾生成相关问题。 5. 引用时自然添加标识。 关键点:同时使用EasyExcelPOI的理由。 - 从引用[1]:EasyExcel自带POI依赖,但版本可能冲突,所以有时需要排除EasyExcelPOI并引入其他版本。 - 从引用[2]:两者各有优缺点。EasyExcel适合处理大文件,避免内存溢出;POI功能更全面,支持各种操作复杂样式。 - 因此,同时使用的理由可能包括: - 项目需要EasyExcel的特定功能(如高效处理大文件),但也需要POI的某些高级功能(如公式、颜色、复杂样式)。 - 由于EasyExcel基于POI,但简化了API,所以开发者可能在某些场景用EasyExcel简化开发,在其他场景用原生POI实现更复杂需求。 - 版本管理:如果项目已经使用了特定版本的POI,而EasyExcel自带同版本,就需要协调。 结构回答: 1. **引言**:简要解释两者关系。EasyExcel是基于POI的封装。 2. **理由一:功能互补**:EasyExcel擅长处理大文件,避免内存溢出;POI提供更全面的功能如公式、加密等。所以项目可能同时使用以覆盖同需求。 3. **理由二:版本兼容**:引用[1]提到版本冲突问题。如果项目其他部分依赖特定POI版本,而EasyExcel自带另一个版本,就需要同时管理或排除依赖。 4. **理由三:逐步迁移或混合使用**:在旧项目中,可能已经用了POI,引入EasyExcel用于新功能或优化性能。 5. **总结**:强调基于具体需求选择。 6. **生成相关问题**:在末尾添加。 确保回答真实可靠:基于引用常识。EasyExcel确实基于POI,所以同时使用是常见的。 添加引用:在相关段落末尾自然添加[^1]或[^2]。 生成相关问题: - 例如: 1. 如何解决EasyExcelPOI的版本冲突? 2. EasyExcelPOI在性能上有什么具体差异? 3. 在实际项目中,如何决定使用EasyExcel还是POI? 最后,用中文回答。</think>### 为什么需要同时使用EasyExcelPOI? 在Java项目中同时使用EasyExcelApache POI(Poor Obfuscation Implementation)通常是为了结合两者的优势,解决特定需求或优化性能。EasyExcel是基于POI的封装库,专注于简化Excel操作提高大文件处理效率,但POI本身提供了更全面的原生功能。以下是同时使用的主要理由,基于实际开发场景逐步分析: #### 1. **功能互补:满足同场景需求** - **EasyExcel的优势**:基于SAX事件模型,适合处理大型Excel文件(如百万行数据),避免内存溢出(OOM)。它简化了API,支持快速导出、分页读取灵活表头设计,特别适合数据导出、导入等批量操作[^2]。 - **POI的优势**:功能更全面,支持Excel公式、单元格颜色、加密/解密、复杂样式(如合并单元格)旧版本Excel(如.xls格式)。POI适合需要精细控制Excel细节的场景,例如生成带公式的报表或动态样式调整[^2]。 - **同时使用理由**:项目可能既需要EasyExcel的高效大数据处理(如导出日志文件),又需要POI实现特定高级功能(如计算单元格公式或添加条件格式)。例如: - 使用EasyExcel处理数据导入导出,确保性能。 - 使用POI补充EasyExcel支持的公式或样式操作。 这样既能提升效率,又能覆盖复杂需求。 #### 2. **版本管理依赖协调** - **版本冲突问题**:EasyExcel通常自带特定版本的POI依赖(如POI 3.17),而项目其他模块可能依赖POI版本(如4.x)。如果直接引入EasyExcel,会导致版本冲突,引发导出失败或运行时错误[^1]。 - **同时使用理由**:通过Maven或Gradle排除EasyExcel的默认POI依赖,并显式引入项目所需的POI版本。例如: ```xml <!-- Maven示例:排除EasyExcelPOI,引入自定义版本 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>3.3.2</version> <exclusions> <exclusion> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> </exclusion> <exclusion> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>5.2.3</version> <!-- 自定义POI版本 --> </dependency> ``` 这样,项目可以同时利用EasyExcel的简化APIPOI的稳定功能,避免兼容性问题[^1]。 #### 3. **性能优化渐进式迁移** - **性能权衡**:EasyExcel在处理大文件时内存占用低(基于流式读取),但无法直接支持公式或颜色;POI功能强大但内存消耗高,易导致OOM[^2]。 - **同时使用理由**:在已有POI的项目中引入EasyExcel,逐步迁移性能敏感模块。例如: - 新功能使用EasyExcel实现高效导出。 - 旧有复杂报表保留POI实现。 这种混合策略减少了重构成本,同时优化了系统资源使用。 #### 总结 同时使用EasyExcelPOI的核心目的是**平衡效率与功能**:EasyExcel提供高性能易用性,POI补充高级控制兼容性。实际决策应基于项目需求: - 优先使用EasyExcel处理大数据量导入/导出。 - 仅在需要公式、样式或旧版Excel支持时使用POI。 - 通过依赖管理解决版本冲突,确保稳定运行[^1][^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值