该文章为本系列的第一篇
第二篇为 : Java POI操作Excel(Event Model)
第三篇为 : Java POI操作Excel(Event User Model)
第四篇为 : 使用POI封装一个轻量级Excel解析框架
前言
在B端系统中,通常都会提供Excel导入导出数据的功能.所以使用Java对Excel进行操作是每个Java Web工程师必备的技能.而在Java领域,Apache POI是优秀的Excel操作库.这篇文章开始,我们就使用POI一步一步的进行Excel的操作,到最后自制一个简单的基于POI的Excel解析框架.
POI简介
POI-HSSF and POI-XSSF - Java API To Access Microsoft Excel Format Files
HSSF is the POI Project's pure Java implementation of the Excel '97(-2007) file format. XSSF is the POI Project's pure Java implementation of the Excel 2007 OOXML (.xlsx) file format.
POI针对目前存在的两种版本的Excel有不同的实现方式.而对于相同版本的Excel又分为Dom解析和Sax解析两种.而这两种在内存使用,性能各方面均有不同.官方文档中给出了具体的比较,如下图前四列.第五列则是对Excel2007版本提供的节约内存的导出方式进行了介绍.
POI UserModel
根据上面的表格我们可以看到,所谓的UserModel实际上就类似于Dom方式的解析.学过Xml解析的朋友都会知道所谓的Dom解析就是将文件全部读入内存,对文件内部的结构进行建模成一颗Dom树的过程.接下来我们看一下POI对Excel的建模的Dom树是什么结构的.
POI 官网Demo
官网中对这个页面的标题是 Busy Developers' Guide to HSSF and XSSF Features
所以我们通过这个网页Demo的学习,即可完成对POI的入门.
快速入门
开发环境
Java版本 :1.8.0_40
Maven版本:3.3.9
POI版本 :3.15
创建一个工作簿(Workbook)
POI中使用Workbook对Excel对象进行建模.
public class Demo01 {
public static void main(String[] args) throws Exception {
Workbook excel1997 = new HSSFWorkbook(); // excel 1997
FileOutputStream fileOut = new FileOutputStream("workbook.xls");
excel1997.write(fileOut);
fileOut.close();
Workbook excel2007 = new XSSFWorkbook(); // excel 2007
fileOut = new FileOutputStream("workbook.xlsx");
excel2007.write(fileOut);
fileOut.close();
}
}
打开创建的Excel
Workbook对象是UserModel解析方式中整个Dom树的根对象.而Workbook类本身是一个接口,规范了Workbook对象的基本功能.所以除非我们是要使用特定版本Excel的特性,一般都是面向Workbook接口编程.
创建一个工作表(sheet)
public static void main(String[] args) throws Exception {
Workbook wb = new HSSFWorkbook();
Sheet sheet1 = wb.createSheet("new sheet");
Sheet sheet2 = wb.createSheet("second sheet");
// Note that sheet name is Excel must not exceed 31 characters
// and must not contain any of the any of the following characters:
// 0x0000
// 0x0003
// colon (:)
// backslash (\)
// asterisk (*)
// question mark (?)
// forward slash (/)
// opening square bracket ([)
// closing square bracket (])
// You can use org.apache.poi.ss.util.WorkbookUtil#createSafeSheetName(String nameProposal)}
// for a safe way to create valid names, this utility replaces invalid characters with a space (' ')
String safeName = WorkbookUtil.createSafeSheetName("[O'Brien's sales*?]"); // returns " O'Brien's sales "
Sheet sheet3 = wb.createSheet(safeName);
FileOutputStream fileOut = new FileOutputStream("workbook02.xls");
wb.write(fileOut);
fileOut.close();
}
打开创建的Excel
Sheet对象是Workbook对象下的一级对象,代表工作表.而通过上面的程序Demo我们已经可以知道在创建Sheet的时候,要注意的工作表的命名约束.以及如何使用安全的方式创建工作表.
创建单元格
public static void main(String[] args) throws Exception {
Workbook wb = new HSSFWorkbook();
//Workbook wb = new XSSFWorkbook();
CreationHelper createHelper = wb.getCreationHelper();
Sheet sheet = wb.createSheet("new sheet");
// Create a row and put some cells in it. Rows are 0 based.
Row row = sheet.createRow((short)0);
// Create a cell and put a value in it.
Cell cell = row.createCell(0);
cell.setCellValue(1);
// Or do it on one line.
row.createCell(1).setCellValue(1.2);
row.createCell(2).setCellValue(
createHelper.createRichTextString("This is a string"));
row.createCell(3).setCellValue(true);
// Write the output to a file
FileOutputStream fileOut = new FileOutputStream("workbook03.xls");
wb.write(fileOut);
fileOut.close();
}
打开创建的Excel
通过代码我们可以知道Sheet下一级的对象不是Cell而是Row,而Row的下一级对象是Cell.在部分代码很好理解.我们重点介绍一下这个RichTextString.Excel中的字符串,都会存在一个Table(数组)中,而在解析Excel的时候我们会发现解析到的文本类型的单元格的value是数字,而这个数字实际上就是该文本在RichText Table中的下标.
小结
至此,我们了解了POI对Excel操作的UserModel时Dom树的整体结构.如下图
而我们在解析Excel的时候,即可按照这种方式,从上到下,对我们需要的数据进行解析.
解析
要解析的Excel
编码
public static void main(String[] args) throws Exception {
InputStream inp = null;
try {
inp = new FileInputStream("workbook04.xls");
Workbook wb = WorkbookFactory.create(inp);
Sheet sheet = wb.getSheetAt(0);
Iterator<Row> rowIterator = sheet.rowIterator();
while (rowIterator.hasNext()) {
Row r = rowIterator.next();
if (r == null) {
System.out.println("Empty Row");
continue;
}
for (int i = r.getFirstCellNum(); i < r.getLastCellNum(); i++) {
Cell cell = r.getCell(i);
String cellValue = "";
switch (cell.getCellType()) {
case Cell.CELL_TYPE_STRING:
cellValue = cell.getRichStringCellValue().getString();
break;
case Cell.CELL_TYPE_NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
cellValue = cell.getDateCellValue().toString();
} else {
cellValue = String.valueOf(cell.getNumericCellValue());
}
break;
case Cell.CELL_TYPE_BOOLEAN:
cellValue = String.valueOf(cell.getBooleanCellValue());
break;
case Cell.CELL_TYPE_FORMULA:
cellValue = String.valueOf(cell.getCellFormula());
break;
case Cell.CELL_TYPE_BLANK:
break;
default:
}
System.out.println("CellNum:" + i + " => CellValue:" + cellValue);
}
}
} finally {
if (inp != null) {
inp.close();
}
}
}
输出结果
CellNum:0 => CellValue:1.0
CellNum:1 => CellValue:Sun Jul 16 00:00:00 CST 2017
CellNum:2 => CellValue:This is a String
小结
- 使用WorkbookFactory来创建Workbook.从而不引入具体的Workbook实现.达到解耦的效果
- 遍历Row和Cell的时候既可以使用Iterator,也可以使用for循环的方式.其中实际有数据的是获取到的最后一行(或一列)的前一行(一列)
- 针对不同类型的CellType,取值方式并不相同.要使用恰当的方式.
总结
这篇文章作为Excel解析的第一篇,对POI进行了简单的介绍.然后通过几个简单小例子对POI的UserModel进行了分析与讲解.总体来说,UserModel方式解析Excel比较简单.但是作为代价,这种方式对内存的占用是不小的.这也是我们在对技术选型时要考虑的因素.
本文只作为Excel解析的入门,并不能涉及UserModel的方方面面,更多内容还需要去官网查看Demo进行学习.而使用文档进行学习也是程序员的必备技能之一.需要我们不断的训练自己这方面的能力.