一、环境配置
我们通过学习知道,java操作Excel时需要在maven工程中的pom.xml文件添加poi-ooxml的依赖包,眼下我们选择的版本是3.17,因为相对稳定且使用率较高。配置路径以及结果如下:
二、poi-ooxml源码类学习
我们安装好依赖后按照业务需要,即操作Excel表格(当前不涉及Excel的写入,只有读取操作),会学习到四个源码类,分别是XSSFWorkbook源码类、XSSFSheet源码类、XSSFRow源码类和XSSFCell源码类。学习源码的一个大体节奏是通过源码类先学习类的构造方法,然后是普通方法最后汇总源码方法,进行流水线开发。现在我先把四个源码类的常用方法总结下来,具体如下。
2.1 XSSFWorkbook源码类[public class XSSFWorkbook extends POIXMLDocument implements Workbook]。(路径:poi-ooxml-3.17.jar-->org.apache.poi.xssf.usermodel-->XSSFWorkbook.clss):
//构造方法
public XSSFWorkbook();
public XSSFWorkbook(XSSFWorkbookType workbookType);
// public XSSFWorkbook(OPCPackage pkg) throws IOException;
// public XSSFWorkbook(InputStream is) throws IOException;
public XSSFWorkbook(File file) throws IOException, InvalidFormatException;
---上面构造方法引出File源码类,通过实例话File类拿到File类的对象,传参,获取XSSFWorkbook的对象。
public XSSFWorkbook(String path) throws IOException;
//普通方法
public XSSFSheet cloneSheet(int sheetNum);
public XSSFSheet cloneSheet(int sheetNum, String newName);
public XSSFSheet createSheet();
public XSSFSheet createSheet(String sheetname);
public XSSFSheet getSheet(String name);
public XSSFSheet getSheetAt(int index);
public int getSheetIndex(String name);
public int getSheetIndex(Sheet sheet);
public String getSheetName(int sheetIx);
public void removeSheetAt(int index);
public void setSheetName(int sheetIndex, String sheetname);
2.2 XSSFSheet源码类[public class XSSFSheet extends POIXMLDocumentPart implements Sheet]。上面我们观察到普通方法内创建Sheet、获取Sheet等方法最终都会得到一个sheet对象,那么XSSFSheet源码类又有哪些方法提供给我们使用呢?
// 构造方法
protected XSSFSheet();
protected XSSFSheet(PackagePart part);
注意:XSSFSheet源码类的构造方法都是被保护类型的,不能直接调用,我们可以通过XSSFWorkbook源码类拿到sheet对象,操作XSSFSheet源码类中的方法。
// 普通方法
public XSSFWorkbook getWorkbook();
public String getSheetName();
public int getColumnWidth(int columnIndex);
public int getDefaultColumnWidth();
public boolean isColumnHidden(int columnIndex);
public void setColumnHidden(int columnIndex, boolean hidden);
public void setColumnWidth(int columnIndex, int width);
public void setDefaultColumnWidth(int width)
public int getFirstRowNum();
public int getLastRowNum();
public short getDefaultRowHeight();
public void setDefaultRowHeight(short height);
public XSSFRow createRow(int rownum);
public XSSFRow getRow(int rownum);
public void removeRow(Row row)
2.3 XSSFRow源码类[public class XSSFRow implements Row, Comparable<XSSFRow>]。上面我们可以看到由XSSFWorkbook类对象操作XSSFSheet类中的方法可以得到XSSFRow类的对象。得到行对象后就可以操作XSSFRow源码类中的方法。
// 构造方法
protected XSSFRow(CTRow row, XSSFSheet sheet);
同XSSFSheet源码类,XSSFRow源码类的构造方法也是被保护的,解决方法类似。
// 普通方法
public XSSFSheet getSheet();
public XSSFCell createCell(int columnIndex);
public XSSFCell createCell(int columnIndex, CellType type);
public XSSFCell getCell(int cellnum);
public short getFirstCellNum();
public short getLastCellNum(); // 特殊点:从1开始计数。
public int getRowNum();
public void setRowNum(int rowIndex);
public void removeCell(Cell cell);
2.4 XSSFCell源码类[public final class XSSFCell implements Cell ]。同样的道理,我们来看一下XSSFCell源码类中都有哪些构造方法和普通方法。
// 构造方法
protected XSSFCell(XSSFRow row, CTCell cell);
// 普通方法
public XSSFSheet getSheet();
public XSSFRow getRow();
public boolean getBooleanCellValue();
public void setCellValue(boolean value);
public double getNumericCellValue();
public void setCellValue(double value);
public String getStringCellValue();
public void setCellValue(String str);
public XSSFRichTextString getRichStringCellValue();
public void setCellValue(RichTextString str);
public String getCellFormula();
public void setCellFormula(String formula);
public Date getDateCellValue();
public void setCellValue(Date value);
public String getErrorCellString() throws IllegalStateException;
public void setCellErrorValue(byte errorCode);
public int getColumnIndex();
public int getRowIndex();
public CellType getCellTypeEnum();
三、现在我们已经总结了以上四个源码类中的方法,下面我们就可以写流水线代码了。
import java.io.IOException;
import java.util.Date;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
//流水线代码
public class TestUtil {
public static void main(String[] args) throws Exception {
XSSFWorkbook wb = new XSSFWorkbook("/Users/xxxxxxxxx/testdata/app_testcase.xlsx");
XSSFSheet getSheet = wb.getSheetAt(3);
XSSFRow getRow = getSheet.getRow(1);
XSSFCell getCell = getRow.getCell(2);
CellType cellType = getCell.getCellTypeEnum();
System.out.println(getSheet.getLastRowNum());
System.out.println(getRow.getFirstCellNum());
System.out.println(getRow.getLastCellNum());
switch(cellType) {
case _NONE:
System.out.println("_NONEvalue为空。");
break;
case NUMERIC:
double numValue = getCell.getNumericCellValue();
System.out.println("numValue = " + numValue);
break;
case STRING:
String strValue = getCell.getStringCellValue();
System.out.println("strValue = " + strValue);
break;
case FORMULA:
String formulaValue = getCell.getCellFormula();
System.out.println("formulaValue = " + formulaValue);
case BLANK:
System.out.println("BLANKvalue为空。");
break;
case BOOLEAN:
boolean booleanValue = getCell.getBooleanCellValue();
System.out.println("booleanValue = " + booleanValue);
break;
case ERROR:
System.out.println("没见过这种格式的单元格。");
break;
default:
Date dateValue = getCell.getDateCellValue();
System.out.println("dateValue = " + dateValue);
break;
}
}
}
四、第一次封装代码。原则上一个源码类我们封装成一个方法,不同的源码类封装成不同的方法。此次封装得到3个方法,分别是:获取workbook、获取sheet、获取单元格的值。
import java.io.IOException;
import java.util.Date;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
//第一次封装流水线代码
public class TestUtil2 {
//私有属性
private String filePath;
//构造方法
public TestUtil2(String filePath) {
this.filePath = filePath;
}
//普通方法,获取workbook
public XSSFWorkbook getWb() throws Exception {
XSSFWorkbook wb = new XSSFWorkbook(this.filePath);
return wb;
}
//普通方法,获取sheet。通过workbook,根据下标拿到excel表格,即sheet。
public XSSFSheet getSheet(int sheetIndex) throws Exception {
XSSFWorkbook wb = getWb();
XSSFSheet sheet = wb.getSheetAt(sheetIndex);
return sheet;
}
//普通方法,获取某一个单元格的值。通过拿到的sheet表格,拿到某一行;通过这一行拿到这一行的某一列的单元格;通过该单元格属性,使用不同方法获取该单元格的值。
public Object getCellValue(int sheetIndex, int rowIndex, int cellIndex) throws Exception {
Object value = null;
XSSFSheet sheet = getSheet(sheetIndex);
XSSFRow row = sheet.getRow(rowIndex);
XSSFCell cell = row.getCell(cellIndex);
CellType cellType = cell.getCellTypeEnum();
switch(cellType) {
case _NONE:
value = "";
break;
case NUMERIC:
value = cell.getNumericCellValue();
break;
case STRING:
value = cell.getStringCellValue();
break;
case FORMULA:
value = cell.getCellFormula();
case BLANK:
value = "";
break;
case BOOLEAN:
value = cell.getBooleanCellValue();
break;
case ERROR:
System.out.println("没见过这种格式的单元格。");
break;
default:
value = cell.getDateCellValue();
break;
}
return value;
}
public static void main(String[] args) throws Exception {
TestUtil2 tu2 = new TestUtil2("/Users/xxxxxxx/testdata/app_testcase.xlsx");
Object value = tu2.getCellValue(1, 1, 3);
System.out.println("第2个表格的第二行第四列的值是:" + value);
}
}
五、第二次封装代码。此次封装主要针对上面获取单元格的值做了拆解。由一个方法拆分成两个方法,抽离出直接通过cell对象就能直接返回单元格的值的方法。
import java.io.IOException;
import java.util.Date;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
//第二次封装流水线代码
public class TestUtil3 {
//私有属性
private String filePath;
//构造方法
public TestUtil3(String filePath) {
this.filePath = filePath;
}
//普通方法,获取workbook
public XSSFWorkbook getWb() throws Exception {
XSSFWorkbook wb = new XSSFWorkbook(this.filePath);
return wb;
}
//普通方法,获取sheet。通过workbook,根据下标拿到excel表格,即sheet。
public XSSFSheet getSheet(int sheetIndex) throws Exception {
XSSFWorkbook wb = getWb();
XSSFSheet sheet = wb.getSheetAt(sheetIndex);
return sheet;
}
//普通方法,获取某一个单元格的值。通过拿到的sheet表格,拿到某一行;通过这一行拿到这一行的某一列的单元格;通过该单元格属性,使用不同方法获取该单元格的值。
public Object getCellValue(int sheetIndex, int rowIndex, int cellIndex) throws Exception {
XSSFSheet sheet = getSheet(sheetIndex);
XSSFRow row = sheet.getRow(rowIndex);
XSSFCell cell = row.getCell(cellIndex);
Object value = fromCellGetCellValue(cell);
return value;
}
//普通方法,获取单元格的值。通过只传一个cell单元格对象就能返回单元格的值
public Object fromCellGetCellValue(XSSFCell cell) {
Object value = null;
CellType cellType = cell.getCellTypeEnum();
switch(cellType) {
case _NONE:
value = "";
break;
case NUMERIC:
value = cell.getNumericCellValue();
break;
case STRING:
value = cell.getStringCellValue();
break;
case FORMULA:
value = cell.getCellFormula();
case BLANK:
value = "";
break;
case BOOLEAN:
value = cell.getBooleanCellValue();
break;
case ERROR:
System.out.println("没见过这种格式的单元格。");
break;
default:
value = cell.getDateCellValue();
break;
}
return value;
}
public static void main(String[] args) throws Exception {
TestUtil3 tu2 = new TestUtil3("/Users/xxxxxxx/testdata/app_testcase.xlsx");
Object value = tu2.getCellValue(1, 1, 3);
System.out.println("第2个表格的第二行第四列的值是:" + value);
}
}
六、第三次封装代码。第三次封装主要是根据二维数组和Excel表格的相似性(都是通过两个坐标来定位一个元素)来把Excel表格的数据存储到二维数组内,再通过二维数组坐标取值,或者通过两层for循环拿到二维数组的全部值。
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
//第三次封装流水线代码
public class TestUtil4 {
//私有属性
private String filePath;
//构造方法
public TestUtil4(String filePath) {
this.filePath = filePath;
}
//普通方法,获取workbook
public XSSFWorkbook getWb() throws Exception {
XSSFWorkbook wb = new XSSFWorkbook(this.filePath);
return wb;
}
//普通方法,获取sheet。通过workbook,根据下标拿到excel表格,即sheet。
public XSSFSheet getSheet(int sheetIndex) throws Exception {
XSSFWorkbook wb = getWb();
XSSFSheet sheet = wb.getSheetAt(sheetIndex);
return sheet;
}
//普通方法,获取某一个单元格的值。通过拿到的sheet表格,拿到某一行;通过这一行拿到这一行的某一列的单元格;通过该单元格属性,使用不同方法获取该单元格的值。
public Object getCellValue(int sheetIndex, int rowIndex, int cellIndex) throws Exception {
XSSFSheet sheet = getSheet(sheetIndex);
XSSFRow row = sheet.getRow(rowIndex);
XSSFCell cell = row.getCell(cellIndex);
Object value = fromCellGetCellValue(cell);
return value;
}
//普通方法,获取单元格的值。通过只传一个cell单元格对象就能返回单元格的值
public Object fromCellGetCellValue(XSSFCell cell) {
Object value = null;
CellType cellType = cell.getCellTypeEnum();
switch(cellType) {
case _NONE:
value = "";
break;
case NUMERIC:
value = cell.getNumericCellValue();
break;
case STRING:
value = cell.getStringCellValue();
break;
case FORMULA:
value = cell.getCellFormula();
case BLANK:
value = "";
break;
case BOOLEAN:
value = cell.getBooleanCellValue();
break;
case ERROR:
System.out.println("没见过这种格式的单元格。");
break;
default:
value = cell.getDateCellValue();
break;
}
return value;
}
//普通方法,获取整个Excel的数据。通过Excel的坐标定位到cell单元格对象,由上面的fromCellGetCellValue方法得到该单元格的值;再定义一个二维数组接收所有单元格的值。
//注意:二位数组的下表要和excel中想要获取的值的下表对应。
public Object[][] getArrayExcelValue(int sheetIndex) throws Exception{
XSSFSheet sheet =getSheet(sheetIndex);
int lastRowNum = sheet.getLastRowNum();
// int lastCellnum = sheet.getl
Object[][] testData = new Object[lastRowNum][11];
for(int i=1;i<lastRowNum+1;i++) {
XSSFRow row = sheet.getRow(i);
if(row==null) {continue;}
for(int j=row.getFirstCellNum();j<row.getLastCellNum();j++) {
XSSFCell cell = row.getCell(j);
if(cell==null) {continue;}
testData[i-1][j] = fromCellGetCellValue(cell);
// testData[i-1][j] = getCellValue(sheetIndex, i, j);
}
}
return testData;
}
//普通方法,输出二维数组
public static void outPutArray(Object[][] data){
for(int i=0;i<data.length;i++) {
for(int j=0;j<data[i].length;j++) {
Object value = data[i][j];
System.out.print(value + " ");
}
System.out.print("\n");
}
}
public static void main(String[] args) throws Exception {
// TestUtil4 tu2 = new TestUtil4("/Users/zhaoxiaodong/testdata/app_testcase.xlsx");
// Object value = tu2.getCellValue(1, 1, 3);
// System.out.println("第2个表格的第二行第四列的值是:" + value);
TestUtil4 tu4 = new TestUtil4("/Users/zhaoxiaodong/testdata/app_testcase.xlsx");
Object[][] testData = tu4.getArrayExcelValue(3);
// System.out.println(testData);
System.out.println(testData[1][1]);
outPutArray(testData);
}
}
完美,完毕!