POI
- apache 的访问 excel 工具
- 03版本 用 HSSFWorkbook,07版本用 XSSFWorkbook,多数据写出时07版本可用 SXSSFWorkbook提升效率
写Excel
Static String PATH="D:\\" //定义excel文件存储路径
new Workbook() 创建工作簿
workBook.createSheet 创建工作表
sheet.createRow 创建行
row.createCell 创建列(单元格)
cell.setCellValue 为单元格赋值
new FileOutputStream(PATH+"xx.xls|xx.xlsx") 创建文件输出流
workBook.write(FileOutputStream) 在指定路径下写出excel文件
FileOutputStream.close() 关闭流
- 多数据写出的时候,使用 SXSSFWorkbook 时会生成临时文件,在关闭流之后可以清除临时文件
((SXSSFWorkbook)Workbook).dispose()
读Excel
Static String PATH="D:\\" //定义excel文件存储路径
new FileInputStream(PATH+"xx.xls|xx.xlsx") 获取文件流
new WorkBook(FileInputStream) 创建工作簿
WorkBook.getSheetAt(0)得到第一张工作表
Sheet.getRow得到行
Row.getCell得到列
Cell.getStringCellValue得到值
FileInputStream.close()关闭流
- 读取值的时候一定要注意类型
Cell.getTypeCellValue
读取不同类型的数据
FileInputStream inputStream = new FileInputStream(PATH + "xx.xls");//文件输入流
Workbook hssfWorkbook = new HSSFWorkbook(inputStream);//获取工作簿
Sheet sheet = hssfWorkbook.getSheetAt(0);//获取第一张表
Row rowTitle = sheet.getRow(0);//获取标题行
if (rowTitle != null) {
int cellCount = rowTitle.getPhysicalNumberOfCells();
for (int i = 0; i < cellCount; i++) {
Cell cell = rowTitle.getCell(i);
if(cell!=null){
String cellValue=cell.getStringCellValue();
System.out.print(cellValue);
}
}
}
int rowCount=sheet.getPhysicalNumberOfRows();
for(int i=1;i<=rowCount;i++){
Row rowData = sheet.getRow(i);
if (rowData != null) {
int cellCount=rowData.getPhysicalNumberOfCells();
for(int j=0;j<=cellCount;j++){
Cell cell=rowData.getCell(i);
if(cell!=null){
CellType cellType =cell.getCellType();
String cellValue="";
switch (cellType){
case STRING:
System.out.println("[String]");
cellValue=cell.getStringCellValue();
break;
case NUMERIC:
System.out.println("numeric");
if(HSSFDateUtil.isCellDateFormatted(cell)){
System.out.println("日期");
Date date = cell.getDateCellValue();
new DateTime(date).toString("yyyy-MM-dd");
}else{
//不是日期格式,防止数字过长
System.out.println("转换为字符串");
cell.setCellType(CellType.STRING);
cellValue=cell.toString();
}
break;
}
System.out.println(cellValue);
}
}
}
}
读取Excel计算公式
FileInputStream inputStream = new FileInputStream(PATH + "xx.xls");//文件输入流
Workbook workbook = new HSSFWorkbook(inputStream);//获取工作簿
Sheet sheet = workbook.getSheetAt(0);//获取第一张表
Row row = sheet.getRow(4);//获取公式所在行
Cell cell = row.getCell(0);//获取公式所在列
FormulaEvaluator evaluator = new HSSFFormulaEvaluator((HSSFWorkbook) workbook);//获取公式
CellType cellType = cell.getCellType();
switch (cellType){
case FORMULA:
String formula = cell.getCellFormula();
System.out.println(formula);
CellValue cellValue = evaluator.evaluate(cell);
System.out.println(cellValue);
break;
}
}
EasyExcel
写Excel
- 写好模板类
@Data
public class DemoData {
@ExcelProperty("字符串标题")
private String string;
@ExcelProperty("日期标题")
private Date date;
@ExcelProperty("数字标题")
private Double doubleData;
/**
* 忽略这个字段
*/
@ExcelIgnore
private String ignore;
}
- 创建测试数据
private List<DemoData> data() {
List<DemoData> list = new ArrayList<DemoData>();
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;
}
- 写测试
public static String PATH = "D:\\";
@Test
public void writeTest() {
String fileName = PATH + "easyTest.xlsx";
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
// 如果这里想使用03 则 传入excelType参数即可
//write(文件路径+文件名,格式类)
//sheet(表名)
//doWrite(数据),实际开发中应该是从前端传过来的数据
EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());
}
读Excel
- 实体类已经创建,跳过该步骤
- 写好监听器,主要步骤为继承AnalysisEventListener,然后重写 invoke方法
package com.bookstore.easyexcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
public class DemoDataListener extends AnalysisEventListener<DemoData> {
private static final Logger LOGGER = (Logger) LoggerFactory.getLogger(DemoDataListener.class);
/**
* 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 5;
private DemoDAO demoDAO;
List<DemoData> list = new ArrayList<DemoData>();
public DemoDataListener() {
// 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
demoDAO = new DemoDAO();
}
public DemoDataListener(DemoDAO demoDAO) {
this.demoDAO = demoDAO;
}
@Override
public void invoke(DemoData data, AnalysisContext context) {
System.out.println("解析到一条数据:"+ 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("存储数据库成功!");
}
}
- 创建数据持久化层(为了逻辑完整,未写具体方法)
package com.bookstore.easyexcel;
import java.util.List;
/**
* 假设这个是你的DAO存储。当然还要这个类让spring管理,当然你不用需要存储,也不需要这个类。
**/
public class DemoDAO {
public void save(List<DemoData> list) {
// 如果是mybatis,尽量别直接调用多次insert,自己写一个mapper里面新增一个方法batchInsert,所有数据一次性插入
}
}
- 读测试
@Test
public void readTest(){
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
// 写法1:
String fileName = PATH + "easyTest.xlsx";
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();
}