这个只是自己初步封装的一个Util类,大家可以根据自己的实际需求进行扩展开发,感觉这个东西应该还是挺有用的。另外中间用到了Mybatis中的一个反射类不要忘了导包。
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.web.multipart.MultipartFile;
public class POIUtils<T> {
private final static String XLS_SUFFIX = "xls_suffix";
private final static String XLSX_SUFFIX = "xlsx";
private final static String DATE_FORMAT = "yyyy/MM/dd";
// 做一个实体类字段类型缓存
private static Map<Class, Map<String, Class>> maps = new HashMap<>();
/**
*
* @param file 通过springmvc controller接收的excel文件
* @param clazz 转换之后的实体Bean类型
* @param <T>
* @return
* @throws Exception
*/
public static <T> List<T> readExcel(MultipartFile file, Class<T> clazz) throws Exception {
// 解析实体bean每个字段对应的数据类型
Map<String, Class> typeMap = getParameterType(clazz);
//检查文件
checkFile(file);
//获得Workbook工作薄对象
Workbook workbook = getWorkBook(file);
//把解析出来的每一行数据封装成一个实体Bean存放到list集合中
List<T> list = new ArrayList<>();
if (workbook != null) {
for (int sheetNum = 0; sheetNum < workbook.getNumberOfSheets(); sheetNum++) {
//获得当前sheet工作表
Sheet sheet = workbook.getSheetAt(sheetNum);
if (sheet == null) {
continue;
}
//获得当前sheet的开始行
int firstRowNum = sheet.getFirstRowNum();
// 获得表头行,表头行对应的是实体类的字段名
Row headerRow = sheet.getRow(firstRowNum);
//获得表头行的开始列
int firstCellNum = headerRow.getFirstCellNum();
//获得表头行的列数
int columnsNum = headerRow.getPhysicalNumberOfCells();
// 获得表头行字段数组,也就是对应的实体Ben的字段数组
String[] columns = new String[columnsNum];
for (int cellNum = firstCellNum; cellNum < columnsNum; cellNum++) {
Cell cell = headerRow.getCell(cellNum);
columns[cellNum] = getCellValue(cell);
}
//获得当前sheet的结束行
int lastRowNum = sheet.getLastRowNum();
//循环除了第一行的所有行,每一行数据获取完毕之后封装成一个实体Bean存到list中
for (int rowNum = firstRowNum + 1; rowNum <= lastRowNum; rowNum++) {
// 通过反射的方式创建一个实体对象,用MyBatis中的类可以方便的实现为属性赋值
MetaObject metaObject = SystemMetaObject.forObject(clazz.newInstance());
//获得当前行
Row row = sheet.getRow(rowNum);
if (row == null) {
continue;
}
String[] cells = new String[columnsNum];
//循环当前行
for (int cellNum = firstCellNum; cellNum < columnsNum; cellNum++) {
Cell cell = row.getCell(cellNum);
// 得到excel表格中的原始的数据(String类型的)
cells[cellNum] = getCellValue(cell);
Class type = typeMap.get(columns[cellNum]);
// 把excel中的数据解析成对应的java的数据类型
Object o = typeHandler(cells[cellNum], type);
// 为bean的字段赋值 等同于调用set方法
metaObject.setValue(columns[cellNum], o);
}
// 每次循环一行数据就往list中添加一条记录
list.add((T) metaObject.getOriginalObject());
}
}
workbook.close();
}
return list;
}
/**
* 校验文件是否合法
*
* @param file
* @throws IOException
*/
public static void checkFile(MultipartFile file) throws IOException {
// 判断文件是否存在
if (null == file || file.isEmpty()) {
throw new FileNotFoundException("空文件");
}
// 获得文件名
String fileName = file.getOriginalFilename();
// 判断文件是否是excel文件
if (!(fileName.endsWith(XLSX_SUFFIX) || fileName.endsWith(XLS_SUFFIX))) {
throw new IOException(fileName + "文件格式错误");
}
}
/**
* 根据不同的文件类型得到对应的工作簿对象
*
* @param file
* @return
* @throws IOException
*/
public static Workbook getWorkBook(MultipartFile file) throws IOException {
//获得文件名
String fileName = file.getOriginalFilename();
//创建Workbook工作薄对象,表示整个excel
Workbook workbook = null;
try (InputStream is = file.getInputStream()) {
//根据文件后缀名不同(xls和xlsx)获得不同的Workbook实现类对象
if (fileName.endsWith(XLS_SUFFIX)) {
//2003
workbook = new HSSFWorkbook(is);
} else if (fileName.endsWith(XLSX_SUFFIX)) {
//2007
workbook = new XSSFWorkbook(is);
}
}
return workbook;
}
/**
* 解析每个单元格中的数据
*
* @param cell
* @return
*/
@SuppressWarnings("deprecation")
public static String getCellValue(Cell cell) {
String cellValue = "";
if (cell == null) {
return cellValue;
}
// 解析日期格式的数据
String dataFormatString = cell.getCellStyle().getDataFormatString();
if (dataFormatString.equals("m/d/yy")) {
cellValue = new SimpleDateFormat(DATE_FORMAT).format(cell.getDateCellValue());
return cellValue;// "yyyy/MM/dd"
}
// 把数字统一转换成String类型进行处理
if (cell.getCellType() == Cell.CELL_TYPE_NUMERIC) {
cell.setCellType(Cell.CELL_TYPE_STRING);
}
// 判断数据的类型
switch (cell.getCellType()) {
case Cell.CELL_TYPE_NUMERIC: // 数字
cellValue = String.valueOf(cell.getNumericCellValue());
break;
case Cell.CELL_TYPE_STRING: // 字符串
cellValue = String.valueOf(cell.getStringCellValue());
break;
case Cell.CELL_TYPE_BOOLEAN: // Boolean
cellValue = String.valueOf(cell.getBooleanCellValue());
break;
case Cell.CELL_TYPE_FORMULA: // 公式
cellValue = String.valueOf(cell.getCellFormula());
break;
case Cell.CELL_TYPE_BLANK: // 空值
cellValue = "";
break;
case Cell.CELL_TYPE_ERROR: // 故障
cellValue = "非法字符";
break;
default:
cellValue = "未知类型";
break;
}
return cellValue;
}
/**
* 字段名与字段类型的对应关系映射
*
* @param clazz 实体Bean
* @param <T>
* @return
*/
public static <T> Map<String, Class> getParameterType(Class<T> clazz) {
// 为每一个实体Bean缓存一个(columnName -> Type)类型的Map放在全局变量中实现缓存的功能,频繁调用反射性能较低
Map<String, Class> map = maps.get(clazz);
if (map != null && map.size() > 0) {
return map;
}
map = new HashMap<>();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
String fieldName = field.getName();
Class fieldType = field.getType();
map.put(fieldName, fieldType);
}
// 对bean的字段类型进行缓存
maps.put(clazz, map);
return map;
}
/**
* 字段类型转换处理器 todo:可以改根据实体类字段进行扩展或者封装一个更加通用的类型转换处理器,可以参考Mybatis的类型处理
*
* @param value 每一个单元格解析出来的原始数据
* @param type 解析之后的类型处理器
* @return 转换之后返回对应的java类型的数据
* @throws ParseException
*/
public static Object typeHandler(String value, Class type) throws ParseException {
if (type == int.class || type == Integer.class) {
return Integer.parseInt(value);
}
if (type == String.class) {
return value;
}
if (type == Date.class) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
Date parse = sdf.parse(value);
return parse;
}
return value;
}
}
另外给大家提供可能要用到的jar包的maven坐标
<dependencies>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.3.0</version>
</dependency>
</dependencies>