POI是Apache下开源、目前比较流行的java操作office的api,对于操作excel,提供了操作03版本及其以前版本(.xls)和07版本及以后版本(.xlsx)的两个接口,其中处理03版的速度会快于07版。
excel 03版与07版的区别在于,03版最多只有65535行数据,07版则没有上限
easyExcel是阿里巴巴下在POI的基础上二次开发的开源api,以使用简单、节省内存著称。(官方文档写的已经非常详细了,就不再这做过多代码的演示)
POI与easyExcel的区别主要在内存消耗上。
POI由于在操作excel时是先将所有数据都读入内存后,再写入文件,比较消耗内存,特别是大数据量时,容易出现OOM
EasyExcel 能大大减少占用内存的主要原因是在解析 Excel 时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。
org.apache.poi
poi
3.9
org.apache.poi
poi-ooxml
3.9
junit
junit
4.12
org.projectlombok
lombok
1.18.20
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
//学号
private Integer stuNo;
//姓名
private String name;
//生日
private Date birthday;
//体重
private Double weight;
//模拟通过数据库获取数据
public static List getStudentList() throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
List students = new LinkedList<>();
students.add(new Student(1,"张三",sdf.parse("1999/8/5"),65.5));
students.add(new Student(2,"李四",sdf.parse("2000/2/8"),60.3));
return students;
}
}
流程:创建工作簿>创建工作表sheet>创建行>创建列>设值>写出文件
注意:03版与07版代码区别在
(1) 创建工作簿实例化对象不同
(2) 文件名后缀不同
(3) 大数据情况下,03版数据量超出65535行会报错,07版可能会出现内存溢出、速度较03版慢
(4) 使用SXSSFWorkbook可提升07版的速度,但要注意清除生成的临时文件
//全局静态变量之后代码不在注明
private static String PATH = "F:\\mavenProject\\excel_POI\\";
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
@Test
public void testWrite() throws Exception{
List students = Student.getStudentList();
//1、创建一个工作簿
//03版
//Workbook workbook = new HSSFWorkbook();
//07版
Workbook workbook = new XSSFWorkbook();
//07加速版,可加速07的操作,但要注意清除临时文件
//Workbook workbook = new SXSSFWorkbook();
//2、创建一个工作表sheet,命名为"模板表"
Sheet sheet = workbook.createSheet("模板表");
//3、创建表头
//3.1、创建第一行
Row row1 = sheet.createRow(0);
//3.2、在第一行中创建第1~4列并设值
row1.createCell(0).setCellValue("序号");
row1.createCell(1).setCellValue("姓名");
row1.createCell(2).setCellValue("生日");
row1.createCell(3).setCellValue("体重");
//4、创建表格内容
for (int i = 0; i < students.size(); i++) {
Student student = students.get(i);
Row row = sheet.createRow(i + 1);
row.createCell(0).setCellValue(student.getStuNo());
row.createCell(1).setCellValue(student.getName());
row.createCell(2).setCellValue(sdf.format(student.getBirthday()));
row.createCell(3).setCellValue(student.getWeight());
}
//03版
//FileOutputStream fileOutputStream = new FileOutputStream(PATH + "模板表.xls");
//07版
FileOutputStream fileOutputStream = new FileOutputStream(PATH + "模板表.xlsx");
//5、写出excel
workbook.write(fileOutputStream);
//关闭流
fileOutputStream.close();
//使用SXSSFWorkbook时, 需要清除临时文件
//((SXSSFWorkbook) workbook).dispose();
System.out.println("excel导出完毕");
}
HSSFWorkbook补充:
优点:可以写非常大的数据量,如 100 万条甚至更多条,写数据速度更快,占用更少内存
注意:
1、过程中会产生临时文件,需要清理临时文件
2、默认由 100 条记录被保存在内存中,如果超过这数量,则最前面的数据被写入临时文件
3、可以使用 new SXSSFWorkbook(数量)自定义内存中的数量
4、仍然可能会消耗较大的内存
@Test
public void testRead() throws Exception {
FileInputStream fileInputStream = new FileInputStream(PATH + "模板表.xlsx");
//1、创建一个工作簿,将流放入其中
Workbook workbook = new XSSFWorkbook(fileInputStream);
//2、获取第一个表sheet
Sheet sheet = workbook.cloneSheet(0);
//3、获取表头
Row rowTitle = sheet.getRow(0);
if (rowTitle != null) {
//获取行的总列数
int cellCount = rowTitle.getPhysicalNumberOfCells();
for (int cellIndex = 0; cellIndex < cellCount; cellIndex++) {
//获取行的每个列
Cell cell = rowTitle.getCell(cellIndex);
if (cell != null) {
//获取值,由于表头都是字符串,就不用进行类型判断
String cellValue = cell.getStringCellValue();
System.out.print(cellValue + "、");
}
}
System.out.println();
}
//获取表格内容
//获取表格的行总数
int totalRows = sheet.getPhysicalNumberOfRows();
//从第二行开始遍历
for (int rowIndex = 1; rowIndex < totalRows; rowIndex++) {
//获取每一行
Row row = sheet.getRow(rowIndex);
if (row != null) {
int totalCells = row.getPhysicalNumberOfCells();
for (int cellIndex = 0; cellIndex < totalCells; cellIndex++) {
//获取每一列
Cell cell = row.getCell(cellIndex);
if (cell != null) {
//获取值的数据类型
int cellType = cell.getCellType();
String cellValue = "";
switch (cellType) {
case Cell.CELL_TYPE_STRING: // 字符串
System.out.print("【String】");
cellValue = cell.getStringCellValue();
break;
case Cell.CELL_TYPE_BOOLEAN: // 布尔类型
System.out.print("【Boolean】");
cellValue = String.valueOf(cell.getBooleanCellValue());
break;
case Cell.CELL_TYPE_BLANK: // 为空
System.out.print("【Blank】");
break;
case Cell.CELL_TYPE_NUMERIC: // 数字(日期、普通数字)类型
System.out.print("【Numeric】");
// 判断当前列是不是时间日期
if (HSSFDateUtil.isCellDateFormatted(cell)) {
System.out.print("【日期】");
Date date = cell.getDateCellValue();
cellValue = new DateTime(date).toString("yyyy-MM-dd");
} else {
// 不是日期格式,防止数字过长
System.out.print("【转换为字符串输出】");
cell.setCellType(Cell.CELL_TYPE_STRING);
cellValue = cell.toString();
}
break;
case Cell.CELL_TYPE_ERROR: // 布尔类型
System.out.print("【数据类型错误】");
cellValue = String.valueOf(cell.getErrorCellValue());
break;
case Cell.CELL_TYPE_FORMULA: // 公式类型,代表这个cell存的是一个公式
System.out.print("【公式】");
// 6、获取计算公式
FormulaEvaluator formulaEvaluator = new XSSFFormulaEvaluator((XSSFWorkbook) workbook);
// 8、得到第 5 行第 1 列 计算公式的内容
String formula = cell.getCellFormula();
// formula = SUM(A2:A4)
System.out.println("formula = " + formula);
// 9、计算得到值 把获取到第 5 行,第 1 列的内容放入到
CellValue evaluate = formulaEvaluator.evaluate(cell);
// 10、将的到的内容转化为字符串
cellValue = evaluate.formatAsString();
break;
}
System.out.println(cellValue);
}
}
}
fileInputStream.close();
}
}