介绍:
为了更好地导入excel数据写了这个工具类。欢迎指正!
可以处理.xls、.xlsx的excel文件。利用了java的反射、和jdk里的poi工具,自动将导入的excel文件转换为对象的集合。
- 更新日志:2020年09月30日,添加上行空值判断。
- 更新日志:2021年08月06日,增加转换成map的功能。
public <T> Map<String, List<T>> streamToMapList(String[] str, InputStream inputStream, String fileName, Class<T> clazz, Map<Integer, int[]> ignorData)
添加这个方法,将数据Map的key是sheet的名称,value是这个sheet的内容
使用
主要方法:
public <T> List<T> ImportExcelUtilV1(String[] str, MultipartFile file, Class<T> aClass, Map<Integer, int[]> ignorData) {
this.str = str;
this.file = file;
this.aClass = aClass;
this.ignorData = ignorData;
return this.streamToList(str, file, aClass, ignorData);
}
- 第一步:定义参数,一共需要四个参数
-
String []
表示的实体类对象的属性的名字。比如一个person
实体类里的name
,age
等。将这些属性名一个个以字符串放到字符串数组中。注意实体类的属性要设置为public
的 -
MultipartFile file
表示传入的文件,用的springmvc接收的文件 -
Class aClass
类类型比如person,使用类.class
或者对象.getClass
-
Map<Integer, int[]> ignorData
表示的是要忽略那一个sheet,哪一些行row。比如我导入一个person的excel,第一个sheet存放数据,第二个sheet存放的注意事项,提醒填表人。那么这个Integer填1,int[]填null
- 第二步 创建工具类对象,将这些准备好的数据放入到方法参数中就可以得到想要的转换为list的数据
ImportExcelUtilV1 import = new ImportExcelUtilV1();
List list =import .ImportExcelUtilV1(str, file, 某个实体类.class, ignorData);
注意事项
- String [] 数组的顺序即为导入的excel每一列的顺序,即如下面的第一列表示proName这个值。
- 只能处理年月日的时间类型。如果输入很长的数值,建议将excel设置为文本格式。
- 实体类的成员变量必须是public的。
- 实体类必须要有get、set方法。
一个例子
- 定义参数以及使用
String[] str = {
"proName", "proCode", "proMngName", "proMngWorkId",
"proLeaderName", "proLeaderWorkId", "proType", "proLevle",
"proPltPlz", "proStatus", "proRate", "conCode",
"conMoney", "conDate", "proSt", "proDt",
"proDesc"};//定义要导入的excel有哪些值和实体类对象对应
Map<Integer, int[]> ignorData = new HashMap<>();
ignorData.put(1, null);//忽略第二个sheet
ignorData.put(0, new int[]{0, 1});//忽略第一个sheet的第一行和第二行
List<ProImportExcelFo> list = null;
try {
list = new ImportExcelUtilV1().ImportExcelUtilV1(str, file, ProImportExcelFo.class, ignorData);//调用匿名对象方法,使用try catch目的在于导入的数据可能为空,汇报空指针异常!
} catch (Exception e) {
return e.getMessage();
}
- 实体类对象例子
实体类中用到的swagger,和lombok - lombok的注解:
@Data
:自动生成get、set方法
@Accessors(chain = true)
:链式表达式 - swagger的注解:
@ApiModelProperty("")
:在swagger上可以查看注释信息
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.Date;
/**
* 项目导入excel的Vo
*
* @Author :yeguojin
* @Date :Created in 2020/9/21 9:31
* @Description:项目导入excel的Vo
* @Modified By:
*/
@Data
@Accessors(chain = true)
public class ProImportExcelFo {
/****/ @ApiModelProperty("") public String proName ;
/****/ @ApiModelProperty("") public String proCode;
/****/ @ApiModelProperty("") public String proMngName;
/****/ @ApiModelProperty("") public String proMngWorkId;
/****/ @ApiModelProperty("") public String proLeaderName;
/****/ @ApiModelProperty("") public String proLeaderWorkId;
/****/ @ApiModelProperty("") public String proType;
/****/ @ApiModelProperty("") public String proLevle;
/****/ @ApiModelProperty("") public String proPltPlz;
/****/ @ApiModelProperty("") public String proStatus;
/****/ @ApiModelProperty("") public Integer proRate;
/****/ @ApiModelProperty("") public Date proSt;
/****/ @ApiModelProperty("") public Date proDt;
/****/ @ApiModelProperty("") public String proDesc;
}
工具类代码
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import org.apache.poi.hssf.usermodel.HSSFDataFormat;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* @Author :yeguojin
* @Date :Created in 2021/8/2 14:53
* @Description:文件导入工具类
* @Modified By:
*/
public class ExcelImportToList {
private final static String ERRORMSG = "传入的String【】和javabean的属性不对应或和excel的列数不对应";
/**
* 2007 版本的excel
*/
private final static String EXCEL_2007 = ".xlsx";
/**
* 2003 版本的excel
*/
private final static String EXCEL_2003 = ".xls";
private final static String STRING = "String";
private final static String INT = "int";
private final static String INTEGER = "Integer";
private final static String LONG = "Long";
private final static String DOUBLE = "Double";
private final static String SHORT = "Short";
private final static String FLOAT = "Float";
private final static String BOOLEAN = "Boolean";
private final static String DATE = "Date";
private final static String BIGDECIMAL = "BigDecimal";
/**
* 全角空格
*/
private final static String FULL_WIDTH_SPACE = " ";
/**
* 半角空格
*/
private final static String HALF_WIDTH_SPACE = " ";
/**
* excel上日期格式的id值
*/
private final static int EXCEL_DATE_FORMAT = 58;
/**
* 单元格:常规
*/
private final static String GENERAL = "General";
/**
* set方法头
*/
private final static String METHODHEADER = "set";
/**
* excel格式有误
*/
private final static String EXCEL_FORMAT_IS_WRONG = "解析文件格式有误!";
/**
* 类型转换出错
*/
private final static String TYPE_CONVERSION_ERROR = "字符串转换没有对应类型";
/**
* 英文逗号
*/
private final static String COMMA = ",";
/**
* 空串
*/
private final static String EMPTY_STRING = "";
/**
* 点
*/
private final static String POINT = ".";
/**
* 时分
*/
private final static String TIME_1 = "h:mm";
/**
* 时分
*/
private final static String TIME_2 = "HH:mm";
/**
* 年月日
*/
private final static String YEAR_MONTH_DAY = "yyyy-MM-dd";
/**
* 月日
*/
private final static String MONTH_DAY = "M月d日";
/**
* 普通格式
*/
private final static String NORMAL_FORMAT = "#.##";
/**
* 定义文件的string[] 属性
*/
private String[] str;
/**
* 定义输入流对象
*/
private InputStream inputStream;
/**
* 文件全名称
*/
private String fileName;
/**
* 实体类对象的类型
*/
private Class clazz;
/**
* 需要忽略的sheet 和 row
* integer 表示忽略的sheet,int [] 表示 忽略的row
*/
private Map<Integer, int[]> ignoreData;
/**
* 参数名称
*/
private Map<String, String> methodNameMap;
public ExcelImportToList() {
}
public ExcelImportToList(String[] str, InputStream inputStream, String fileName, Class clazz, Map<Integer, int[]> ignoreData) {
this.str = str;
this.inputStream = inputStream;
this.fileName = fileName;
this.clazz = clazz;
this.ignoreData = ignoreData;
this.methodNameMap = new HashMap<>();
/* 根据str 设置方法名称*/
for (String s : str) {
this.methodNameMap.put(s, METHODHEADER + s.substring(0, 1).toUpperCase() + s.substring(1));
}
}
public <T> List<T> excelImportToList(String[] str, InputStream inputStream, String fileName, Class<T> clazz, Map<Integer, int[]> ignoreData) throws Exception {
this.str = str;
this.inputStream = inputStream;
this.fileName = fileName;
this.clazz = clazz;
this.ignoreData = ignoreData;
this.methodNameMap = new HashMap<>(str.length);
/*根据str 设置方法名称*/
for (String s : str) {
this.methodNameMap.put(s, METHODHEADER + s.substring(0, 1).toUpperCase() + s.substring(1));
}
return this.streamToList(str, inputStream, fileName, clazz, ignoreData);
}
public <T> List<T> streamToList() throws Exception {
return this.streamToList(this.str, this.inputStream, this.fileName, this.clazz, this.ignoreData);
}
/**
* 将输入流转换为javaBean的list
*
* @param str 字符串数组
* @param inputStream excel的输入流
* @param fileName excel的全名称
* @param clazz javabean的类型
* @param ignorData 忽略的sheet和行
* @param <T>
* @return javabean的list
* @throws Exception
*/
private <T> List<T> streamToList(String[] str, InputStream inputStream, String fileName, Class<T> clazz, Map<Integer, int[]> ignorData) throws Exception {
List<T> list = new ArrayList<T>();
Set set = ignorData.keySet();
Workbook workbook = getWorkbook(inputStream, fileName);
if (workbook == null) {
return new ArrayList<>();
}
Row firstRow = this.getFirstRow(workbook, ignorData);
if (firstRow == null) {
return list;
}
if (!isEqualBetweenBeanAndStr(str, clazz, firstRow)) {
throw new RuntimeException(ERRORMSG);
}
int sheetNum = workbook.getNumberOfSheets();
/*循环sheet*/
for (int i = 0; i < sheetNum; i++) {
Sheet sheet = workbook.getSheetAt(i);
if (set.contains(i)) {
int[] ints = ignorData.get(i);
if (ints == null) {
continue;
}
Set<Integer> set1 = new HashSet<Integer>();
for (int o = 0; o < ints.length; o++) {
set1.add(ints[o]);
}
/*todo丢失的情况*/
int fsaf = sheet.getLastRowNum();
for (int j = sheet.getFirstRowNum(); j < sheet.getLastRowNum() + 1; j++) {
/*循环row*/
Row row = sheet.getRow(j);
if (this.isRowEmpty(row)) {
continue;
}
T obj = clazz.newInstance();
if (set1.contains(Integer.valueOf(j))) {
continue;
}
for (int t = row.getFirstCellNum(); t < row.getLastCellNum(); t++) {
/*循环cell单元格*/
Cell cell = row.getCell(t);
String methodName = methodNameMap.get(str[t]);
Field field = clazz.getField(str[t]);
Method method = clazz.getMethod(methodName, field.getType());
method.invoke(obj, getValue(getCellToStr(cell), field));
}
list.add(obj);
}
}
}
return list;
}
/**
* 将输入流转换为javaBean的list 分为一个sheet对应一个lsit,map的key就是sheet的名称
*
* @param str 字符串数组
* @param inputStream excel的输入流
* @param fileName excel的全名称
* @param clazz javabean的类型
* @param ignorData 忽略的sheet和行
* @param <T>
* @return javabean的list key为sheet的名称 如果sheet1和sheet?同名称 则放在同一个key下
* @throws Exception
*/
public <T> Map<String, List<T>> streamToMapList(String[] str, InputStream inputStream, String fileName, Class<T> clazz, Map<Integer, int[]> ignorData) throws Exception {
Map<String, List<T>> listMap = new HashMap<>();
/*根据str 设置方法名称*/
this.methodNameMap = new HashMap<>(str.length);
for (String s : str) {
this.methodNameMap.put(s, METHODHEADER + s.substring(0, 1).toUpperCase() + s.substring(1));
}
Set set = ignorData.keySet();
Workbook workbook = getWorkbook(inputStream, fileName);
if (workbook == null) {
return new HashMap<>();
}
Row firstRow = this.getFirstRow(workbook, ignorData);
if (firstRow == null) {
return listMap;
}
if (!isEqualBetweenBeanAndStr(str, clazz, firstRow)) {
throw new RuntimeException(ERRORMSG);
}
int sheetNum = workbook.getNumberOfSheets();
/*循环sheet*/
for (int i = 0; i < sheetNum; i++) {
Sheet sheet = workbook.getSheetAt(i);
List<T> list = new ArrayList<T>();
if (set.contains(i)) {
int[] ints = ignorData.get(i);
if (ints == null) {
continue;
}
Set<Integer> set1 = new HashSet<Integer>();
for (int o = 0; o < ints.length; o++) {
set1.add(ints[o]);
}
/*todo丢失的情况*/
int fsaf = sheet.getLastRowNum();
for (int j = sheet.getFirstRowNum(); j < sheet.getLastRowNum() + 1; j++) {
/*循环row*/
Row row = sheet.getRow(j);
if (this.isRowEmpty(row)) {
continue;
}
T obj = clazz.newInstance();
if (set1.contains(Integer.valueOf(j))) {
continue;
}
for (int t = row.getFirstCellNum(); t < row.getLastCellNum(); t++) {
/*循环cell单元格*/
Cell cell = row.getCell(t);
String methodName = methodNameMap.get(str[t]);
Field field = clazz.getField(str[t]);
Method method = clazz.getMethod(methodName, field.getType());
method.invoke(obj, getValue(getCellToStr(cell), field));
}
list.add(obj);
}
}
if (listMap.get(sheet.getSheetName()) != null) {
listMap.get(sheet.getSheetName()).addAll(list);
} else {
listMap.put(sheet.getSheetName(), list);
}
}
return listMap;
}
/**
* 文件校验 返回文件类型,更具文件类型创建book
*
* @param inputStream 输入流
* @param fileName 文件全名称
* @return 将输入流转换为wookbook,对应2007和2003版的wookBook
* @throws Exception
*/
public Workbook getWorkbook(InputStream inputStream, String fileName) throws Exception {
Workbook work = null;
String fileType = fileName.substring(fileName.lastIndexOf(POINT));
if (EXCEL_2003.equals(fileType)) {
/*2003 版本的excel*/
work = new HSSFWorkbook(inputStream);
} else if (EXCEL_2007.equals(fileType)) {
/*2007 版本的excel*/
work = new XSSFWorkbook(inputStream);
} else {
throw new Exception(EXCEL_FORMAT_IS_WRONG);
}
return work;
}
/**
* 校验str属性是否和实体类相对应,存在某个字段导入为空,但是长度不能大于str 【】的长度
*
* @param str 字符串数组
* @param clazz javabean类型
* @param row excel导入的行
* @return
*/
private Boolean isEqualBetweenBeanAndStr(String[] str, Class clazz, Row row) {
Field[] fields = clazz.getFields();
int rowNum = row.getLastCellNum() - row.getFirstCellNum();
if (!(str.length == fields.length && str.length >= rowNum)) {
return Boolean.FALSE;
}
ArrayList<String> strsList = new ArrayList<>();
List list = Arrays.asList(str);
list.forEach(a -> {
strsList.add((String) a);
});
List<String> fieldsList = new ArrayList<>();
for (int i = 0; i < fields.length; i++) {
fieldsList.add(fields[i].getName());
}
List<String> strsListTemp = new ArrayList<>(strsList);
List<String> fieldsListTemp = new ArrayList<>(fieldsList);
strsList.removeAll(fieldsList);
fieldsListTemp.removeAll(strsListTemp);
if (!(strsList.size() == 0 && fieldsListTemp.size() == 0)) {
return Boolean.FALSE;
}
return Boolean.TRUE;
}
/**
* 值的转化 将excel导入的字符串转换为int Long这一类型 并返回
*
* @param str excel导入的字符串
* @param field 反射获取的javabean的field
* @return excel导入的字符串转换为对应javabean成员的类型
* @throws Exception
*/
private Object getValue(String str, Field field) throws Exception {
if (str == null) {
return null;
}
if (EMPTY_STRING.equals(strFormat(str))) {
return null;
}
String typeName = field.getType().getSimpleName();
if (STRING.equalsIgnoreCase(typeName)) {
return strFormat(str);
}
if (INT.equalsIgnoreCase(typeName) || INTEGER.equalsIgnoreCase(typeName)) {
return Integer.valueOf(strFormat(str));
}
if (LONG.equalsIgnoreCase(typeName)) {
return Long.valueOf(strFormat(str));
}
if (DOUBLE.equalsIgnoreCase(typeName)) {
return Double.valueOf(strFormat(str));
}
if (SHORT.equalsIgnoreCase(typeName)) {
return Short.valueOf(strFormat(str));
}
if (FLOAT.equalsIgnoreCase(typeName)) {
return Float.valueOf(strFormat(str));
}
if (BOOLEAN.equalsIgnoreCase(typeName)) {
return Boolean.valueOf(strFormat(str));
}
if (DATE.equalsIgnoreCase(typeName)) {
return getStrToDate(strFormat(str));
}
if (BIGDECIMAL.equalsIgnoreCase(typeName)) {
return new BigDecimal(strFormat(str));
}
throw new ParseException(TYPE_CONVERSION_ERROR, 0);
}
/**
* 将str转换为date格式
*
* @param str 日期格式字符串
* @return str--》date
* @throws ParseException
*/
private Date getStrToDate(String str) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat(YEAR_MONTH_DAY);
return sdf.parse(str);
}
/**
* 返回该单元格的字符串 空串 返回null
*
* @param cell excel的cell
* @return 单元格的字符串
*/
private String getCellToStr(Cell cell) {
if (cell == null) {
return null;
}
String stringCellValue = cell.toString();
if (EMPTY_STRING.equals(stringCellValue.replaceAll(FULL_WIDTH_SPACE, EMPTY_STRING).replaceAll(HALF_WIDTH_SPACE, EMPTY_STRING))) {
return null;
}
String result = new String();
switch (cell.getCellType()) {
/*Excel公式*/
case Cell.CELL_TYPE_FORMULA:
try {
result = String.valueOf(cell.getNumericCellValue());
} catch (IllegalStateException e) {
result = String.valueOf(cell.getRichStringCellValue());
}
break;
/* 数字类型*/
case Cell.CELL_TYPE_NUMERIC:
/* 处理日期格式、时间格式*/
if (HSSFDateUtil.isCellDateFormatted(cell)) {
SimpleDateFormat sdf;
if (cell.getCellStyle().getDataFormat() == HSSFDataFormat
.getBuiltinFormat(TIME_1)) {
sdf = new SimpleDateFormat(TIME_2);
}
/* 日期*/
else {
sdf = new SimpleDateFormat(YEAR_MONTH_DAY);
}
Date date = cell.getDateCellValue();
result = sdf.format(date);
} else if (cell.getCellStyle().getDataFormat() == EXCEL_DATE_FORMAT) {
/* 处理自定义日期格式:m月d日(通过判断单元格的格式id解决,id的值是58)*/
SimpleDateFormat sdf = new SimpleDateFormat(MONTH_DAY);
double value = cell.getNumericCellValue();
Date date = org.apache.poi.ss.usermodel.DateUtil
.getJavaDate(value);
result = sdf.format(date);
} else {
double value = cell.getNumericCellValue();
CellStyle style = cell.getCellStyle();
DecimalFormat format = new DecimalFormat();
String temp = style.getDataFormatString();
/*单元格设置成常规*/
if (GENERAL.equals(temp)) {
format.applyPattern(NORMAL_FORMAT);
}
result = format.format(value);
}
break;
/* String类型*/
case Cell.CELL_TYPE_STRING:
result = cell.getRichStringCellValue().toString();
break;
case Cell.CELL_TYPE_BLANK:
result = EMPTY_STRING;
default:
result = EMPTY_STRING;
break;
}
return result;
}
private String strFormat(String str) {
/*去掉所有空格 (全角 和半角 空格),去掉所有逗号*/
return str.replaceAll(HALF_WIDTH_SPACE, EMPTY_STRING).replaceAll(FULL_WIDTH_SPACE, EMPTY_STRING).replaceAll(COMMA, EMPTY_STRING);
}
private Row getFirstRow(Workbook workbook, Map<Integer, int[]> ignorData) {
Row row = null;
Set<Integer> set = ignorData.keySet();
int sheetNum = workbook.getNumberOfSheets();
for (int i = 0; i < sheetNum; i++) {
Sheet sheet = workbook.getSheetAt(i);
if (set.contains(i)) {
if (ignorData.get(i) == null) {
continue;
}
Set<Integer> set1 = new HashSet<Integer>();
for (int o = 0; o < ignorData.get(i).length; o++) {
set1.add(ignorData.get(i)[o]);
}
for (int m = sheet.getFirstRowNum(); m < sheet.getLastRowNum() + 1; m++) {
if (set1.contains(m)) {
continue;
}
row = sheet.getRow(m);
return row;
}
} else {
row = sheet.getRow(0);
return row;
}
}
return row;
}
/**
* 空行的判断
*
* @param row excel的行
* @return 是否空行 空为true
*/
private Boolean isRowEmpty(Row row) {
if (row == null) {
return true;
}
for (Cell c : row) {
if (c.getCellType() != Cell.CELL_TYPE_BLANK) {
return false;
}
}
return true;
}
/**
* 重复名称判断
*
* @param stringList
* @return
*/
public List<String> repeatList(List<String> stringList) {
if (stringList != null && stringList.isEmpty()) {
return null;
}
List<String> repeatList = new ArrayList<>();
Map<String, Integer> map = new HashMap<>();
for (String str : stringList) {
//定义一个计数器,用来记录重复数据的个数
int i = 1;
if (map.get(str) != null) {
i = map.get(str) + 1;
}
map.put(str, i);
}
Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Integer> next = iterator.next();
if (next.getValue() > 1) {
repeatList.add(next.getKey());
}
}
return repeatList.isEmpty() ? null : repeatList;
}
public String[] getStr() {
return str;
}
public void setStr(String[] str) {
this.str = str;
}
public InputStream getInputStream() {
return inputStream;
}
public void setInputStream(InputStream inputStream) {
this.inputStream = inputStream;
}
public Class getClazz() {
return clazz;
}
public void setClazz(Class clazz) {
this.clazz = clazz;
}
public Map<Integer, int[]> getIgnoreData() {
return ignoreData;
}
public void setIgnoreData(Map<Integer, int[]> ignoreData) {
this.ignoreData = ignoreData;
}
public Map<String, String> getMethodNameMap() {
return methodNameMap;
}
public void setMethodNameMap(Map<String, String> methodNameMap) {
this.methodNameMap = methodNameMap;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
}