一、前言
gogogo话不多说,如果还没了解过poi的话建议小伙伴可以先了解下poi。
二、什么是POI?
POI是Apache软件基金会用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java程序对Microsoft Office格式档案读和写的功能。所以POI的主要功能是可以用Java操作Microsoft Office的相关文件,但是一般我们都是用来操作Excel相关文件。
三、代码实现
1.首先呢,我们先导入坐标,引入jar
<!-- excel -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.0.1</version>
</dependency>
<!-- poi -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.0.1</version>
</dependency>
2.相关类
pojo----User
@Data
public class User {
@FileAttributes(value = "ID")
private String id;
@FileAttributes(value = "名字")
private String username;
@FileAttributes(value = "密码")
private String password;
}
注解----FileAttributes
/**
* @description 用于标识对应字符对应文件上的属性
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface FileAttributes {
String value();
String defualtValue() default "";
boolean isNull() default true;
Class type() default String.class;
}
工具类---- ParsingFileUtils
/**
* Created by gyk on 2020/9/8
*/
public class ParsingFileUtils {
//文件后缀
private static final String FILE_CONTENT_TYPE_XLSX="xlsx";
private static final String FILE_CONTENT_TYPE_XLS="xls";
private static Logger logger= LoggerFactory.getLogger(ParsingFileUtils.class);
/**
* 将Excel文件转换成list集合
* @param in 文件输入流
* @param suffix 文件后缀
* @param clazz 文件转换对象类型
* @return list集合
* @throws IOException
* @throws IllegalAccessException
* @throws InstantiationException
*/
public static List<Object> parsingFileXls(InputStream in,String suffix,Class clazz) throws IOException, IllegalAccessException, InstantiationException {
//获得所有属性对象
Field[] fields = clazz.getDeclaredFields();
ConcurrentHashMap<String,Integer> titlesMap=new ConcurrentHashMap<>(8);
Workbook book;
try {
//判断文件类型,进行对象构造
if (FILE_CONTENT_TYPE_XLSX.equals(suffix)) {
book = new XSSFWorkbook(in);
} else if (FILE_CONTENT_TYPE_XLS.equals(suffix)) {
book = new HSSFWorkbook(in);
}else {
throw new IllegalArgumentException("file type error!");
}
}catch (IOException e){
logger.error(e.getLocalizedMessage());
throw e;
}
//此处获取第一个sheet页进行操作,如需要多sheet页操作,将其变成for循环
Sheet sheet =book.getSheetAt(0);
//从类中属性注解进行字段-标题映射
for (Field field:fields){
//field 代表类的成员变量
// getAnnotation(Class<A> annotationClass)返回该类中与参数类型匹配的公有注解对象
FileAttributes fileAttributes = field.getAnnotation(FileAttributes.class);
if (fileAttributes==null){
continue;
}
titlesMap.put(fileAttributes.value(),-1);
}
//将字段对应标题所述的列进行映射 getRow(sheet.getFirstRowNum())获取第一行标题
mappingColumns(titlesMap,sheet.getRow(sheet.getFirstRowNum()));
List<Object> result =new ArrayList<>(8);
//遍历所有行,进行添加值。
for (int i=sheet.getFirstRowNum()+1;i<sheet.getLastRowNum()+1;i++){
//获取行数
Row row = sheet.getRow(i);
//创建类的实例
Object target = clazz.newInstance();
//解析每一行,并将列属性给对象赋值
parsingRow(titlesMap, row, fields, target);
if(target!=null) {
result.add(target);
}
}
return result;
}
/**
* @param titleMap 列与字段映射集合
* @param row 列标题
* @return
*/
private static void mappingColumns(ConcurrentHashMap<String,Integer> titleMap, Row row){
for (int i=row.getFirstCellNum();i<row.getLastCellNum();i++){
//获取当前单元格
Cell cell = row.getCell(i);
Integer integer = titleMap.get(cell.getStringCellValue());
if (integer != null){
//重新赋值 key(标题) 的value会被重新覆盖 之前都是-1
titleMap.put(cell.getStringCellValue(),i);
}
}
}
/**
*
* @param titleMap 列与字段映射集合
* @param row 每一行数据
* @param fields 目标对象属性集合
* @param target 目标对象
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
private static void parsingRow(ConcurrentHashMap<String,Integer> titleMap, Row row, Field[] fields, Object target) throws IllegalArgumentException, IllegalAccessException {
for (Field field:fields){
//field 代表类的成员变量
FileAttributes fieldAnnotation = field.getAnnotation(FileAttributes.class);
if (fieldAnnotation == null){
continue;
}
//作用就是让我们在用反射时 可以访问private修饰的私有属性
field.setAccessible(true);
if (!StringUtils.isEmpty(fieldAnnotation.defualtValue())){
field.set(target,fieldAnnotation.defualtValue());
}
//通过标题名拿到对应标题的value索引值
Integer integer = titleMap.get(fieldAnnotation.value());
if (integer==-1){
if (!fieldAnnotation.isNull()&&StringUtils.isEmpty(fieldAnnotation.defualtValue())){
throw new IllegalArgumentException("we need "+fieldAnnotation.value()+" ,but the excel doesn't!");
}
continue;
}
//根据下标获取这一行某个单元格
Cell cell = row.getCell(integer);
//o 接受的是匹配类型后的单元格内容
Object o = parsingCell(cell, fieldAnnotation);
//表格中数据会把整数读取为小数点,这里控制
if(o!=null && o.getClass()==String.class &&((String)o).contains(".0")){
o=((String)o).split("\\.")[0];
}
if (o==null&&!fieldAnnotation.isNull()){
throw new IllegalArgumentException("the ("+row.getRowNum()+","+integer+")cell Not allowed to be empty");
}
//对target进行赋值
field.set(target,o);
}
}
/**
*
* @param cell 单元格
* @param fileAttributes 注解类型
* @return 注解中type属性类型
*/
private static Object parsingCell(Cell cell, FileAttributes fileAttributes){
if (cell==null){
return null;
}
Class type = fileAttributes.type();
if (type ==Date.class){
return cell.getDateCellValue();
}else if(type== boolean.class){
return cell.getBooleanCellValue();
}else if(type==double.class){
return cell.getNumericCellValue();
}else if(type== RichTextString.class){
return cell.getRichStringCellValue();
}else if(type==Integer.class){
String stringCellValue = cell.getStringCellValue();
return Integer.parseInt(stringCellValue);
} else{
try {
return cell.getStringCellValue();
}catch (Exception e){
if (cell.getCellType()==CellType.NUMERIC) {
return ""+cell.getNumericCellValue();
}
return "";
}
}
}
}
四、代码实现了,是不是已经迫不及待想试试水了呢
接下来我们测试一下
用户表.xlsx:
五、解析过程
我们来写一个优雅的main方法测试一下这个工具类行不行
public static void main(String[] args) throws IOException, IllegalAccessException, InstantiationException {
List<Object> objects = parsingFileXls(new FileInputStream(new File("G:\\用户表.xlsx")), FILE_CONTENT_TYPE_XLSX, User.class);
objects.forEach(System.out::println);
}
果然行!!!
User(id=1.0, username= 张三, password=2sd3tg5)
User(id=2.0, username= 李四, password=42freg43)
User(id=3.0, username= 王五, password=2d3d)
User(id=4.0, username= 赵六, password= asdad)
User(id=5.0, username= 刘七, password= sefwe)
User(id=6.0, username= kk, password= 21edwq)
User(id=7.0, username= sx, password= dewr3r)
User(id=8.0, username= aa, password= wegt6y)
User(id=9.0, username= bb, password= qwed2qw)
User(id=10.0, username= cc, password= 21edwq)
收好你们的对象噢