POI.
Apache POI是Apache软件基金会的开放源码函式库,POI提供API给Java程序对Microsoft Office格式档案读和写的功能。
基本功能.
结构:
HSSF
- 提供读写[Microsoft Excel](https://baike.baidu.com/item/Microsoft Excel)格式档案的功能。
XSSF
- 提供读写Microsoft Excel OOXML格式档案的功能。
HWPF
- 提供读写[Microsoft Word](https://baike.baidu.com/item/Microsoft Word)格式档案的功能。
HSLF
- 提供读写Microsoft PowerPoint格式档案的功能。
HDGF
- 提供读写[Microsoft Visio](https://baike.baidu.com/item/Microsoft Visio)格式档案的功能。
准备工作:.
导入依赖.
<!-- xls 03 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.2</version>
</dependency>
<!-- xlsx 07 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
<!-- 日期格式化工具 -->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10.4</version>
</dependency>
<!-- test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
Excel对象简单介绍.
Workbook : 工作簿
Sheet : 工作页
Row : 行
Cell : 单元格
Excel操作方式介绍.
POI提供了HSSF、XSSF以及SXSSF三种方式操作Excel
HSSF
Excel97-2003版本,扩展名为.xls。一个sheet最大行数65536,最大列数256。
缺点:最多只能处理65536行,否则会抛出异常
优点:过程中写入缓存,不操作磁盘,最后一次性写入磁盘,速度快
XSSF
Excel2007版本开始,扩展名为.xlsx。一个sheet最大行数1048576,最大列数16384。
缺点:写数据时速度非常慢,非常耗内存,也会发生内存溢出,如100万条
优点:可以写较大的数据量,如20万条
SXSSF
是在XSSF基础上,POI3.8版本开始提供的支持低内存占用的操作方式,扩展名为.xlsx。
优点:可以写非常大的数据量,如100万条甚至更多条,写数据速度快,占用更少的内存
注意:
- 过程中会产生临时文件,需要清理临时文件
- 默认由100条记录被保存在内存中,如果超过这数量,则最前面的数据被写入临时文件
- 如果想自定义内存中数据的数量,可以使用new SXSSFWorkbook ( 数量 )
SXSSFWorkbook-来至官方的解释:实现“BigGridDemo”策略的流式XSSFWorkbook版本。这允许写入非常大的文件而不会耗尽内存,因为任何时候只有可配置的行部分被保存在内存中。
请注意,仍然可能会消耗大量内存,这些内存基于您正在使用的功能,例如合并区域,注释……仍然只存储在内存中,因此如果广泛使用,可能需要大量内存。
POI-Excel写.
v03
简单使用.
package read._03;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.junit.Test;
import java.io.*;
// poi(03版) 进行 Excel 写操作
public class ExcelWrite {
private final String V03_OUT = "v03_out.xls";
private final String resourcePath = System.getProperty("user.dir") + "\\src\\main\\resources\\";
@Test
public void testWrite() throws IOException {
// 1、创建一个Excel03 工作簿对象
HSSFWorkbook workbook = new HSSFWorkbook();
// 2.创建工作表对象
HSSFSheet poi_user = workbook.createSheet("poi_user");
// 3.创建 行对象
HSSFRow row = poi_user.createRow(0);// 第一行
// 4.创建 单元格
HSSFCell A1 = row.createCell(0);
A1.setCellValue("A1"); // 字符串
HSSFCell B1 = row.createCell(1);
B1.setCellValue(12); // 整型
HSSFCell C1 = row.createCell(2);
C1.setCellValue(13.13); // 浮点
HSSFCell D1 = row.createCell(3);
String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
String datetime = new DateTime().toString("yyyy-MM-dd HH:mm:ss");
D1.setCellValue(datetime); // 时间(字符串格式)
// 文件输出流
FileOutputStream fos = new FileOutputStream(new File(resourcePath + V03_OUT)); // 使用完关闭流
// 5.写出数据到文件
workbook.write(fos);
// 关闭流
fos.close();
}
}
v07
简单使用.
// poi(07版) 进行 Excel 写操作
public class ExcelWrite {
private final String V07_OUT = "v07_out.xlsx";
private final String resourcePath = System.getProperty("user.dir") + "\\src\\main\\resources\\";
@Test
public void testWrite() throws IOException {
// 1.获取工作簿对象 v07
XSSFWorkbook workbook = new XSSFWorkbook();
// 2.获取工作表
XSSFSheet poi_user = workbook.createSheet("poi_user");
// 3.获取行
XSSFRow row = poi_user.createRow(1);// 第二行
// 4.单元格
XSSFCell A2 = row.createCell(0);
A2.setCellValue("A2"); // 字符串
XSSFCell B2 = row.createCell(1);
B2.setCellValue(22); // 整型
XSSFCell C2 = row.createCell(2);
C2.setCellValue(23.23); // 浮点
XSSFCell D2 = row.createCell(3);
String datetime = new DateTime().toString("yyyy-MM-dd HH:mm:ss");
D2.setCellValue(datetime); // 时间字符串
// 文件输出流
FileOutputStream fos = new FileOutputStream(new File(resourcePath+V07_OUT));
// 5.写出数据到文件
workbook.write(fos);
// 关闭流
fos.close();
}
}
v03
HSSF大文件写.
// poi 03版 HSSF 大文件(65536行)写demo
public class ExcelWrite {
private final String V03_HSSF_BIGFILE = "v03_hssf_bigfile_out.xls";
private String resourcePath = System.getProperty("user.dir") + "\\src\\main\\resources\\";
@Test
public void testWrite() throws IOException {
long startTime = System.currentTimeMillis();
// 1.获取工作簿
HSSFWorkbook workbook = new HSSFWorkbook();
// 2.创建工作表
HSSFSheet hssf = workbook.createSheet("hssf");
// 3.创建行(共65536行,如果超过,会报错)
for (int i = 0; i < 65537 ; i++) {
HSSFRow row = hssf.createRow(i);
// 4.创建单元格,写入数据
for (int j = 0; j < 5; j++) {
HSSFCell cell = row.createCell(j);
cell.setCellValue(""+i+j);
}
}
// 文件输出流
FileOutputStream fos = new FileOutputStream(new File(resourcePath+V03_HSSF_BIGFILE));
// 5.写出数据到文件
workbook.write(fos);
// 关闭流
fos.close();
long endTime = System.currentTimeMillis();
System.out.println("hssf大文件写耗费时间:" + (endTime - startTime) + "ms");
}
}
java.lang.IllegalArgumentException: Invalid row number (65536) outside allowable range (0..65535) // 当写入数据行数超过65536时,抛出异常
v07
XSSF大文件写.
// poi 07版 XSSF 大文件(65537行)写demo
public class ExcelWrite {
private final String V07_XSSF_BIGFILE = "v07_xssf_bigfile_out.xlsx";
private String resourcePath = System.getProperty("user.dir") + "\\src\\main\\resources\\";
@Test
public void testWrite() throws IOException {
long startTime = System.currentTimeMillis();
// 1.获取工作簿
XSSFWorkbook workbook = new XSSFWorkbook();
// 2.创建工作表
XSSFSheet xssf = workbook.createSheet("xssf");
// 3.创建行(共65537行)
for (int i = 0; i < 65537; i++) {
XSSFRow row = xssf.createRow(i);
// 4.创建单元格
for (int j = 0; j < 5; j++) {
XSSFCell cell = row.createCell(j);
cell.setCellValue(""+i+j);
}
}
// 创建文件输出流
FileOutputStream fos = new FileOutputStream(new File(resourcePath+V07_XSSF_BIGFILE));
// 5.输出数据到文件
workbook.write(fos);
// 关闭流
fos.close();
long endTime = System.currentTimeMillis();
System.out.println("xssf大文件写耗费时间:" + (endTime - startTime) + "ms");
}
}
v07
SXSSF大文件写.
// 07 版 SXSSF 大文件写demo (比XSSF占用内存少,写入更快)
public class ExcelWrite {
private final String V07_SXSSF_BIGFILE = "v07_sxssf_bigfile_out.xlsx";
private String resourcePath = System.getProperty("user.dir") + "\\src\\main\\resources\\";
@Test
public void testWrite() throws IOException {
long startTime = System.currentTimeMillis();
// 1.获取工作簿
SXSSFWorkbook workbook = new SXSSFWorkbook();
// 2.创建工作表
SXSSFSheet sxssf = workbook.createSheet("sxssf");
// 3.创建行 (65537)
for (int i = 0; i < 65537; i++) {
SXSSFRow row = sxssf.createRow(i);
for (int j = 0; j < 5; j++) {
// 4.创建单元格
SXSSFCell cell = row.createCell(j);
cell.setCellValue(""+i+j);
}
}
// 文件输出流
FileOutputStream fos = new FileOutputStream(new File(resourcePath+V07_SXSSF_BIGFILE));
// 5.数据写出
workbook.write(fos);
// 关闭流
fos.close();
// 6.清理临时文件
workbook.dispose();
long endTime = System.currentTimeMillis();
System.out.println("sxssf大文件写耗费时间:" + (endTime - startTime) + "ms");
}
}
POI-Excel读.
v03
读.
public class ExcelRead {
private final String fileName = "v03_hssf_bigfile_out.xls";
@Test
public void testRead() throws IOException {
// 输入流
InputStream is = this.getClass().getClassLoader().getResourceAsStream(fileName);
// 1.获取工作簿(输入流)
HSSFWorkbook workbook = new HSSFWorkbook(is);
// 2.获取工作表对象
HSSFSheet hssf = workbook.getSheet("hssf");// ①按名字
workbook.getSheetAt(0); // ②按索引
workbook.sheetIterator(); // ③索引迭代器
workbook.getNumberOfSheets(); // 获取索引数
// 3.获取行对象
HSSFRow row = hssf.getRow(0);// ①按索引
hssf.getTopRow(); // ②获取顶行
hssf.rowIterator(); // ③行迭代器
// 4.获取单元格对象
HSSFCell cell = row.getCell(0);
row.cellIterator(); // 单元格迭代器
row.getPhysicalNumberOfCells();// 行单元格数
// 5.输出单元内容
String stringCellValue = cell.getStringCellValue(); // 字符串
/*
boolean booleanCellValue = cell.getBooleanCellValue(); // 布尔
Date dateCellValue = cell.getDateCellValue(); // 日期
byte errorCellValue = cell.getErrorCellValue(); // 字节(错误码)
double numericCellValue = cell.getNumericCellValue(); // 浮点or整型
HSSFRichTextString richStringCellValue = cell.getRichStringCellValue(); // 富文本(除文本,标点外,还可以包含其他元素)
LocalDateTime localDateTimeCellValue = cell.getLocalDateTimeCellValue(); // 本地时间
*/
// 6.打印单元格内容
System.out.println(stringCellValue);
// 关闭流
is.close();
}
}
v07
读.
public class ExcelRead {
private final String fileName = "v07_xssf_bigfile_out.xlsx";
@Test
public void testRead() throws IOException {
// 输入流
InputStream is = this.getClass().getClassLoader().getResourceAsStream(fileName);
// 1.获取工作簿(输入流)
XSSFWorkbook workbook = new XSSFWorkbook(is);
// 2.获取工作表对象
XSSFSheet xssf = workbook.getSheet("xssf");// ①按名字
workbook.getSheetAt(0); // ②按索引
workbook.sheetIterator(); // ③索引迭代器
workbook.getNumberOfSheets(); // 获取索引数
// 3.获取行对象
XSSFRow row = xssf.getRow(0);// ①按索引
xssf.getTopRow(); // ②获取顶行
xssf.rowIterator(); // ③行迭代器
// 4.获取单元格对象
XSSFCell cell = row.getCell(0);
row.cellIterator(); // 单元格迭代器
row.getPhysicalNumberOfCells();// 行单元格数
// 5.输出单元内容
String stringCellValue = cell.getStringCellValue(); // 字符串
/*
boolean booleanCellValue = cell.getBooleanCellValue(); // 布尔
Date dateCellValue = cell.getDateCellValue(); // 日期
byte errorCellValue = cell.getErrorCellValue(); // 字节(错误码)
double numericCellValue = cell.getNumericCellValue(); // 浮点or整型
HSSFRichTextString richStringCellValue = cell.getRichStringCellValue(); // 富文本(除文本,标点外,还可以包含其他元素)
LocalDateTime localDateTimeCellValue = cell.getLocalDateTimeCellValue(); // 本地时间
*/
// 6.打印单元格内容
System.out.println(stringCellValue);
// 关闭流
is.close();
}
}
简单读取Demo.
类型判断、计算公式
计算公式步骤:(前提,cellType=FORMULA).
①获取公式计算器
FormulaEvaluator formulaEvaluator = new XSSFFormulaEvaluator(workbook);
②得到公式名
String cellFormula = cell.getCellFormula();
③进行公式计算
CellValue evaluate = formulaEvaluator.evaluate(cell);
④获取计算结果(字符串)
cellValue = evaluate.formatAsString();
读取Excel的表内容.
数据表.
实现源码.
public class Read {
private static String fileName = "readDemo.xlsx";
public static void main(String[] args) throws IOException {
// 输入流
InputStream is = Read.class.getClassLoader().getResourceAsStream(fileName);
// 1.工作簿
XSSFWorkbook workbook = new XSSFWorkbook(is);
// 2.获取工作表
XSSFSheet demo = workbook.getSheet("demo");
// 3.获取第一行(内容的标题行)
XSSFRow titleRow = demo.getRow(0);
Iterator<Cell> titles = titleRow.cellIterator();
while (titles.hasNext()) {
Cell title = titles.next();
System.out.print(title.getStringCellValue()+"\t\t");
}
// 3.获取其他行(内容的记录行)
int recordNum = demo.getPhysicalNumberOfRows();
for (int i = 1; i < recordNum; i++) {
System.out.println(); // 换行
XSSFRow record = demo.getRow(i);
Iterator<Cell> data = record.cellIterator();
while (data.hasNext()) {
Cell cell = data.next();
String cellValue = "";
switch (cell.getCellType()){ // 判断单元格类型,根据不同类型进行打印操作
case BLANK: // 空单元格 == ""
break;
case BOOLEAN: // 布尔
cellValue = String.valueOf(cell.getBooleanCellValue());
break;
case ERROR: // 错误
cellValue = String.valueOf(cell.getErrorCellValue());
break;
case NUMERIC: // 数值
//HSSFDateUtil.isCellDateFormatted(cell); // @deprecated
if (DateUtil.isCellDateFormatted(cell)) { // 日期格式
cellValue = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(cell.getDateCellValue());
} else { // 非日期的数值类型
cellValue = String.valueOf(cell.getNumericCellValue());
}
break;
case STRING: // 字符串
cellValue = String.valueOf(cell.getStringCellValue());
break;
case FORMULA: // 公式
// 1.公式计算器
FormulaEvaluator formulaEvaluator = new XSSFFormulaEvaluator(workbook);
// 2.得到公式
String cellFormula = cell.getCellFormula();
System.out.print(cellFormula);
// 3.计算公式
CellValue evaluate = formulaEvaluator.evaluate(cell);
// 4.计算的结果
cellValue = evaluate.formatAsString();
break;
default:
throw new RuntimeException("不知道的类型:" + cell.getCellType());
}
//打印单元格
System.out.print(cellValue+"\t\t");
}
}
// 关闭流
is.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模式。在上层做了模型转换的封装,让使用者更加简单方便
相较于POI,更省内存,使用更方便
准备工作:.
看实际使用情况,准备
普通maven
项目.
非web项目,或者 Jsp+servlet的web项目
导入依赖.
<!-- easy-excel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.7</version>
</dependency>
<!-- lombok 需要安装插件 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
<!-- test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.70</version>
</dependency>
<!-- jul(java.util.logging) -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.25</version>
</dependency>
数据库创建.
create database if not exists `poi`;
use poi;
create table `demo`(
`string` varchar(20),
`date` datetime default current_timestamp,
`double_data` double
)
编写db.properties
.
文件存放在
resources
目录下
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/poi?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
username=root
password=mysql
编写 ConnectionUtil
.
public class ConnectionUtil {
private static Connection conn = null;
private static String driver = null;
private static String url = null;
private static String password = null;
private static String username = null;
static {
InputStream is = ConnectionUtil.class.getClassLoader().getResourceAsStream("db.properties");
Properties properties = new Properties();
try {
properties.load(is);
driver = properties.getProperty("driver");
url = properties.getProperty("url");
password = properties.getProperty("password");
username = properties.getProperty("username");
} catch (IOException e) {
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static Connection getConn(){
try {
Class.forName(driver); // 加载驱动类
conn = DriverManager.getConnection(url,username,password);
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}
public static void closeConn(Connection connection){
if (connection != null) {
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
编写BaseDao
.
public class BaseDao {
protected void query(Connection connection, String sql, Object... params){
PreparedStatement ps = null;
ResultSet rs = null;
if (connection != null) {
try {
ps = connection.prepareStatement(sql); // 预编译sql
// 设置占位符对应的值
if (params != null) {
for (int i = 1; i <= params.length; i++) {
ps.setObject(i,params[i-1]);
}
}
rs = ps.executeQuery(); // 执行查询
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
if (ps != null) {
try {
// 关闭连接 释放资源
ps.close();
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
}
protected void execute(Connection connection,String sql,Object... params){
PreparedStatement ps = null;
if (connection != null) {
try {
ps = connection.prepareStatement(sql);
// 设置占位符对应的值
if (params != null) {
for (int i = 1; i <= params.length; i++) {
ps.setObject(i, params[i-1]);
}
}
if (ps.executeUpdate()>0) { // 执行更新操作(insert、update、delete)
System.out.println("操作成功");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
if (ps != null) {
try {
ps.close();
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
}
}
数据准备.
read.xls
文件存放在resources
目录下
字符串 | 日期 | 浮点数 |
---|---|---|
字符串xx | 2021/2/14 | 1.3 |
整合springboot
.
创建
springboot
项目
数据准备.
read-demo.xlsx
文件存放在resources
目录下
id | age | name | version | deleted | create_time | update_time | 公式 |
---|---|---|---|---|---|---|---|
1 | 18 | 张三 | 1 | 0 | 2021/1/3 0:00 | 2021/1/3 0:00 | =PI() |
2 | 19 | 李四 | 1 | 0 | 2021/1/3 0:00 | 2021/1/3 0:00 | |
3 | 20 | 王五 | 1 | 0 | 2021/1/3 0:00 | 2021/1/3 0:00 |
快速入门.
EasyExcel-读.
简单读.
- 创建实体类
- 创建监听器
- EasyExcel.read()….读取Excel文件
DemoData
实体类.
@Data
@AllArgsConstructor
@NoArgsConstructor
public class DemoData {
private String string;
private Date date;
private Double doubleData;
}
DemoDAO
持久类.
public class DemoDAO extends BaseDao {
public void save(List<DemoData> list) {
Connection conn = ConnectionUtil.getConn();
StringBuilder value = new StringBuilder();
if (list != null) {
for (DemoData demoData : list) {
value.append("(");
String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(demoData.getDate());
value.append("'"+demoData.getString()+"','"+date+"',"+demoData.getDoubleData());
value.append("),");
}
} else {
throw new RuntimeException("list不能为空");
}
if (!StringUtils.isEmpty(value.toString())){
execute(conn,"insert into demo(string,date,double_data) values"+value.substring(0, value.length() - 1));
}
}
}
DemoDataListener
监听器(官方).
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
class DemoDataListener extends AnalysisEventListener<DemoData> {
private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class);
/**
* 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 5;
List<DemoData> list = new ArrayList<DemoData>();
/**
* 假设这个是一个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) {
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("存储数据库成功!");
}
}
测试.
public class TestRead {
String fileName = System.getProperty("user.dir") + "\\src\\main\\resources\\read.xls";
InputStream is = TestRead.class.getClassLoader().getResourceAsStream("read.xls");
// 简单读 (写法一)
@Test
public void simpleRead_1() {
// 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
EasyExcel.read(is, DemoData.class, new DemoDataListener()).sheet(0).doRead();
}
// 简单读 (写法二)
@Test
public void simpleRead_2() {
ExcelReader excelReader = null;
try {
excelReader = EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).build();
ReadSheet readSheet = EasyExcel.readSheet(0).build();
excelReader.read(readSheet);
} finally {
// 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的
if (excelReader != null) {
excelReader.finish();
}
}
}
}
结果展示.
指定列的下标或者列名.
涉及注解:@ExcelProperty
.
public @interface ExcelProperty {
// 标题名
String[] value() default {""};
// 索引
int index() default -1;
// 排序
int order() default Integer.MAX_VALUE;
// 格式转换
Class<? extends Converter> converter() default AutoConverter.class;
// use {@link com.alibaba.excel.annotation.format.DateTimeFormat}
@Deprecated
String format() default "";
}
修改实体类,为字段添加注解指定下标或名字
@Data
@AllArgsConstructor
@NoArgsConstructor
public class DemoData {
@ExcelProperty("字符串")
private String string;
@ExcelProperty("浮点数")
private Double doubleData;
@ExcelProperty(index = 1)
private Date date;
}
读多个sheet.
// 读取全部
// DemoDataListener的doAfterAllAnalysed 会在每个sheet读取完毕后调用一次。然后所有sheet都会往同一个DemoDataListener里面写
EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).doReadAll();
// 读取部分
fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
ExcelReader excelReader = null;
try {
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();
// 这里注意 一定要把sheet1 sheet2 一起传进去,不然有个问题就是03版的excel 会读取多次,浪费性能
excelReader.read(readSheet1, readSheet2);
} finally {
if (excelReader != null) {
// 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的
excelReader.finish();
}
}
日期、数字或自定义格式转换.
@Data
public class ConverterData {
/**
* 我自定义 转换器,不管数据库传过来什么 。我给他加上“自定义:”
*/
@ExcelProperty(converter = CustomStringStringConverter.class)
private String string;
/**
* 这里用string 去接日期才能格式化。我想接收年月日格式
*/
@DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")
private String date;
/**
* 我想接收百分比的数字
*/
@NumberFormat("#.##%")
private String doubleData;
}
转换器.
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);
}
}
@Test
public void converterRead() {
String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
// 这里 需要指定读用哪个class去读,然后读取第一个sheet
EasyExcel.read(fileName, ConverterData.class, new ConverterDataListener())
// 这里注意 我们也可以registerConverter来指定自定义转换器, 但是这个转换变成全局了, 所有java为string,excel为string的都会用这个转换器。
// 如果就想单个字段使用请使用@ExcelProperty 指定converter
// .registerConverter(new CustomStringStringConverter())
// 读取sheet
.sheet().doRead();
}
EasyExcel-写.
EasyExcel-填充.
扩展:.
POI SXSSF使用注意.
https://blog.csdn.net/lipinganq/article/details/53434884
- SXSSF通过限制对滑动窗口中的行的访问来实现其低内存占用
- 默认窗口大小windowSize为100,由SXSSFWorkbook.DEFAULT_WINDOW_SIZE定义。
- 可以通过新的SXSSFWorkbook(int windowSize)在工作簿构建时指定窗口大小
SXSSFWorkbook wb1 = new SXSSFWorkbook(100); - 也可以通过SXSSFSheet#setRandomAccessWindowSize(int windowSize)
- windowSize为-1表示无限制访问。在这种情况下,所有尚未通过调用flushRows()刷新的记录可用于随机访问。
- 当通过createRow()创建一个新行并且未刷新记录的总数超过指定的窗口大小时,具有最低索引值的行将被刷新,并且不能再通过getRow()访问。
比如窗口行数为100,内存当前有100行,createRow()创建一个新行,索引值为0的那一行被刷新到本地文件,该行将无法访问,因为它们已写入磁盘。 - SXSSF分配临时文件,您必须始终清除显式,通过调用dispose方法
SXSSFWorkbook wb2 = new SXSSFWorkbook(100);
……
wb2.dispose(); - SXSSFWorkbook默认使用内联字符串而不是共享字符串表(SharedStringsTable)。这是非常有效的,因为没有文档内容需要保存在存储器中,但是也已知生成与一些客户端不兼容的文档。
- 启用共享字符串时,文档中的所有唯一字符串都必须保存在内存中。根据文档内容,这可能使用比禁用共享字符串更多的资源。
- 在决定是否启用共享字符串之前,仔细查看内存预算和兼容性需求
/**
* workbook - 模板工作簿
* rowAccessWindowSize - 保存在内存中,直到刷新的行数。
* compressTmpFiles - 是否对临时文件使用gzip压缩
* useSharedStringsTable - 是否使用共享字符串表
*/
SXSSFWorkbook(XSSFWorkbook workbook, int rowAccessWindowSize, boolean compressTmpFiles, boolean useSharedStringsTable)1234567
- 根据使用的功能,仍然有可能会消耗大量内存的内容,例如合并区域,超链接,注释,…仍然仅存储在存储器中,因此如果广泛使用可能需要大量存储器。