该部分学习代码托管地址:https://gitee.com/imdongrui/study-poi
简介
Apache POI是一个用于支持Java程序操作Excel文件的jar包
从maven引入
<!--必须要引入的包-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.2</version>
</dependency>
<!--需要操作XSSFWorkbook,即xlsx时,需要引入poi-ooxml-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
导出Excel文件
实现步骤
使用poi导出Excel文件我们可以将其分为三个步骤:
- 创建工作簿实例,工作簿实例即对应一个Excel文件;
- 创建Sheet工作表,并向工作表中写入数据;
- 输出Excel文件到指定位置;
我们将这几个步骤体现到代码中:
1 创建工作簿实例
在创建时需要注意,Workbook有两种,分别是org.apache.poi.hssf.usermodel.HSSFWorkbook与org.apache.poi.xssf.usermodel.XSSFWorkbook,其中org.apache.poi.xssf.usermodel.HSSFWorkbook对应的是Office97-2003的早期Excel版本,org.apache.poi.xssf.usermodel.XSSFWorkbook对应的是Excel 2007以上的版本
不能简单地理解为HSSFWorkbook对应xls,XSSFWorkbook对应xlsx,因为单纯通过后缀不足以判断一个Excel文件是什么版本的,因为Excel在打开表格文件时,并没有严格要求后缀与版本对应,即使版本与后缀不对应,仍然可以打开使用,但却会影响poi的解析,这在程序开发时需要注意
/**
* 生成工作簿实例
*
* @return 工作簿实例
*/
public static Workbook createExcel() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
return (Workbook) Class.forName("org.apache.poi.hssf.usermodel.HSSFWorkbook").newInstance();
}
2 创建Sheet并写入数据
/**
* 创建工作表,多个Sheet则调用此函数多次
*
* @param sheetName 工作表名称,若传null,则使用默认的名称
* @param titles 表头
* @param dataSet 数据集
* @param workbook 工作簿实例
* @return 工作表实例
*/
public static Sheet createSheet(String sheetName, String[] titles, String[][] dataSet, Workbook workbook) {
// 创建Sheet实例
Sheet sheet = null == sheetName ? workbook.createSheet() : workbook.createSheet(sheetName);
// 创建表头行,并依次向表头行的表格写入表头数据
Row row = sheet.createRow(0);// 创建数据航
for (int i = 0; i < titles.length; i++) {
Cell cell = row.createCell(i);// 创建单元格
cell.setCellValue(titles[i]);// 写入数据
}
// 循环写入数据集中的数据
int rows = dataSet.length;
int cols = dataSet[0].length;
// 依次处理数据行
for (int rowNum = 0; rowNum < rows; rowNum++) {
Row tmpRow = sheet.createRow(rowNum + 1);//因为表头行占用一行,此处+1
// 依次处理数据列
for (int colNum = 0; colNum < cols; colNum++) {
Cell cell = tmpRow.createCell(colNum);
cell.setCellValue(dataSet[rowNum][colNum]);
}
}
return sheet;
}
3 输出Excel文件到指定路径
/**
* 输出Excel文件到指定路径
*
* @param filePath 输出路径(含文件名)
* @param workbook 当前工作簿实例
*/
public static void outputExcel(String filePath, Workbook workbook) throws IOException {
OutputStream outputStream = new FileOutputStream(filePath);
try {
workbook.write(outputStream);
} finally {
outputStream.close();
}
}
完整调用步骤
通过下面的调用步骤,可以在指定路径下创建一个test.xls文件,并写入我们需要的数据
String[] headers = {"编号", "姓名", "班级", "入学时间"};
// students是一个Student列表,Student是一个简单的数据对象
String[][] dataSet = ExcelUtils.list2Matrix(students, Student.class);
Workbook workbook = ExcelUtils.createExcel();
// 此处创建两个Sheet
ExcelUtils.createSheet("student", headers, dataSet, workbook);
ExcelUtils.createSheet("student2", headers, dataSet, workbook);
ExcelUtils.outputExcel("D:\\test.xls", workbook);
辅助函数
list2Matrix
/**
* 将数据对象的List转换为字符串的二维数组
*
* @param data 数据对象的List
* @param tClass 数据对象的class
* @param <T> 数据对象
* @return 二维数组
*/
public static <T> String[][] list2Matrix(List<T> data, Class<T> tClass) throws IllegalAccessException {
Field[] fields = tClass.getDeclaredFields();
String[][] dataSet = new String[data.size()][fields.length];
// 依次处理列表中的对象
for (int i = 0, len = data.size(); i < len; i++) {
T t = data.get(i);
// 依次获取对象声明的字段值,并存入dataSet中
for (int j = 0; j < fields.length; j++) {
fields[j].setAccessible(true);
Object value = fields[j].get(t);
// 对Date类型的数据进行处理,其它类型数据有处理需求也可在此添加处理逻辑
if (value instanceof Date) {
value = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(value);
}
dataSet[i][j] = value.toString();
}
}
return dataSet;
}
导入Excel文件中的数据
实现步骤
导入Excel文件中的数据也可以分为两个步骤:
- 导入Excel文件并生成Workbook实例;
- 读取Sheet中的数据,多个Sheet则多次读取;
下面将步骤反映到代码中:
1 导入并生成工作簿实例
public enum ExcelType {
HSSF("org.apache.poi.hssf.usermodel.HSSFWorkbook"),
XSSF("org.apache.poi.xssf.usermodel.XSSFWorkbook");
ExcelType(String className) {
this.className = className;
}
private String className;
public String getClassName() {
return className;
}
}
/**
* 从指定路径下的文件创建工作簿实例
* @param path Excel文件路径
* @param type Excel文件的类型
* @return 工作簿实例
*/
public static Workbook createWorkbook(String path, ExcelType type) throws IOException {
// 创建文件输入流
FileInputStream fileInputStream = new FileInputStream(path);
// 根据Excel文件的类型,生成对应的工作簿实例
switch (type) {
case HSSF:
return new HSSFWorkbook(fileInputStream);
case XSSF:
return new XSSFWorkbook(fileInputStream);
default:
throw new RuntimeException();
}
}
2 从Sheet中读取数据
/**
* 读取Sheet中的数据
* @param workbook 工作簿实例
* @param sheetNo 要读取的Sheet编号
* @param tClass 目标数据对象的class
* @param <T> 目标数据对象
* @return 读取出来的目标数据对象列表
*/
public static <T> List<T> readSheet(Workbook workbook, int sheetNo, Class<T> tClass) throws IllegalAccessException, InstantiationException, ParseException {
List<T> list = new ArrayList<>();
// 获取指定的Sheet实例
Sheet sheet = workbook.getSheetAt(sheetNo);
Field[] fields = tClass.getDeclaredFields();
// 跳过表头,从第一行数据开始解析,sheet.getPhysicalNumberOfRows()获取当前Sheet中数据总行数
for (int i = 1, rowCount = sheet.getPhysicalNumberOfRows(); i < rowCount; i++) {
Row row = sheet.getRow(i);// 获取指定行
T t = tClass.newInstance();
// 解析每行数据,row.getPhysicalNumberOfCells()获取当前行下单元格总数
for (int j = 0, cellCount = row.getPhysicalNumberOfCells(); j < cellCount; j++) {
Cell cell = row.getCell(j);// 获取指定单元格
String strValue = cell.getStringCellValue();
fields[j].setAccessible(true);
Class<?> fieldClass = fields[j].getType();
// 按照各个字段类型,处理赋值
if (fieldClass == Integer.class || fieldClass == int.class) {
fields[j].setInt(t, Integer.parseInt(strValue));
} else if (fieldClass == Boolean.class || fieldClass == boolean.class) {
fields[j].setBoolean(t, Boolean.parseBoolean(strValue));
} else if (fieldClass == Double.class || fieldClass == double.class) {
fields[j].setDouble(t, Double.parseDouble(strValue));
} else if (fieldClass == String.class) {
fields[j].set(t, strValue);
} else if (fieldClass == Date.class) {
fields[j].set(t, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(strValue));
}
}
list.add(t);
}
return list;
}
完整调用步骤
Workbook workbook = ExcelUtils.createWorkbook("D:\\test.xlsx", ExcelType.XSSF);
List<Student> list = ExcelUtils.readSheet(workbook, 0, Student.class);
list.forEach(student -> System.out.println(student.toString()));