这里的动态Excle模板文件是有特指的,比如,用户导入一个Excle文件,第一次导入可能是"姓名、性别、年龄…",第二次导入可能是"姓名、身份证号、联系电话…",可以看到导入信息虽然发生了变化,但是总结起来都是人员的基本信息。前一篇文章介绍了Excle模板动态导出,动态导入其实是依赖上一步实现的,虽然是动态导出,但是用户只能在规定的大的字段范围里选取自己想要导出的字段,这样导入的时候只需要判断字段名,通过反射赋值给对象就行了,即使是模板不同,只要在字段范围内,都可以处理。核心步骤如下:
1.导入maven依赖(maven依赖跟上一篇文章一样)
2.前端js代码:
var danWeiId = $("#danWei").combobox("getValue");
//除了excle文件外,还有其他的信息,所以使用FormData创建序列化对象
var formData = new FormData();
formData.append("uploadfile", $("#uploadExcelFile").filebox("files")[0]);
formData.append("danWeiId", danWeiId);
// 还可以在前端列出excel的所有工作空间名称让用户选择需要导入具体导入哪一个或哪几个
formData.append("sheetNames", sn);
// 发送ajax请求
$.ajax({
url : baseurl + '/dengji/saveDengJiInfoByExcle.do',
type : "post",
data : formData,
processData : false,
contentType : false,
dataType : "json",
success : function(data) {
}
});
3.声明字段枚举类:(字段固定,只能在这个范围里选取)
public enum FieldEnum {
/**
* 姓名
*/
XING_MING("姓名", false, "xingMing", "TR"),
/**
* 身份证号
*/
SHEN_FEN_ZHENG_HAO("身份证号", false, "shenFenZhengHao", "TR"),
/**
* 性别
*/
XING_BIE("性别", true, "xingBie", "TR"),
/**
* 出生日期
*/
CHU_SHENG_RI_QI("出生日期", false, "chuShengRiQi", "TR"),
/**
* 年龄
*/
NIAN_LING("年龄", false, "dengJiNianLing", "TS"),
/**
* 联系地址
*/
LIAN_XI_DI_ZHI("联系地址", false, "lianXiDiZhi", "TS");
/*省略其他可能的属性*/
/**
* 字段名称
*/
private String name;
/**
* 是否是字典值
*/
private boolean isZiDian;
/**
* 对应实体类字段属性名称
*/
private String entityFieldName;
/**
* 实体类类型(用于标识是哪个实体类的字段)
*/
private String type;
private FieldEnum(String name, boolean isZiDian, String entityFieldName, String type) {
this.name = name;
this.isZiDian = isZiDian;
this.entityFieldName = entityFieldName;
this.type = type;
}
/**
* 通过name获取枚举类
*
* @param name
* @return
*/
public static FieldEnum getFieldEnum(String name) {
FieldEnum[] values = FieldEnum.values();
for (int i = 0; i < values.length; i++) {
if (values[i].getName().equals(name)) {
return values[i];
}
}
return null;
}
public String getName() {
return name;
}
public boolean isZiDian() {
return isZiDian;
}
public String getEntityFieldName() {
return entityFieldName;
}
public String getType() {
return type;
}
}
4.导入Excle文件:(controller层)
@Autowired
private HttpServletRequest request;
/**
* EXCEL文件导入登记
*
* @param uploadfile 上传文件信息
* @param danWeiId
* @param sheetNames EXCEL工作表空间名称
* @return
*/
@RequestMapping(value = "/saveDengJiInfoByExcle.do", produces = "application/json;charset=UTF-8")
@ResponseBody
// 使用@RequestParam注解绑定参数,否则参数可能绑定失败
public Map<String, Object> saveDengJiInfoByExcle(@RequestParam(value = "uploadfile") MultipartFile uploadfile,
@RequestParam(value = "danWeiId") String danWeiId, @RequestParam(value = "sheetNames") String sheetNames) {
Map<String, Object> resultMap = new HashMap<>();
if (uploadfile != null) {
if (!uploadfile.isEmpty()) {// 判断上传Excle文件对象是否为空
try {
TShenQingEntity tSq = new TShenQingEntity();
tSq.setDanWeiId(danWeiId);
// 模拟从request中获取条件,比如登录用户所属的科室等
// tSq.setCondition(request.getRequestURL().toString());
Map<String, Object> shenQingMap = ExcelImportMuBanUtil.getExcelInfo(uploadfile, tSq, sheetNames);
Boolean errorFlag = (Boolean)shenQingMap.get("errorFlag");
// 判断是否有错误,不合法的格式存在
if (errorFlag != null && errorFlag) {// 如果存在则在前端页面给出提示信息,你也可以保存获取的正确的信息
return shenQingMap;
}
// 保存到数据库中
resultMap = dengjiService.saveTuanTiShenQing(shenQingMap);
} catch (Exception e) {
e.printStackTrace();
logger.error("上传excel文件异常!", e);
}
}
}
return resultMap;
}
5.处理Excle文件导入工具类:
public class ExcelImportMuBanUtil {
/**
* 申请所有成员变量
*/
private static final Field[] T_SHEN_QING_FIELD;
/**
* 人员所有成员变量
*/
private static final Field[] T_USER_FIELD;
static {
T_SHEN_QING_FIELD = TShenQingEntity.class.getDeclaredFields();
T_USER_FIELD = TUser.class.getDeclaredFields();
}
/**
* 验证sheet页前几行没有数据,则当错误sheet处理
*/
private static final int CHECK_SHEET_ROWNUM = 3;
/**
* 获取excel文件中有效的工作表空间名称
*
* @param fileName 文件名
* @param is 流
* @param titleList 标题列表(判断是否存在不合法的sheet)
* @return
*/
public static Map<String, Object> listSheetName(String fileName, InputStream is, List<String> titleList)
throws Exception {
Map<String, Object> resultMap = new HashMap<>();
List<String> errorSheetNames = new ArrayList<String>();
List<String> correctSheetNames = new ArrayList<>();
// 判断其兼容版本 调用了判断版本的方法
Workbook workbook = ExcelCommonUtil.getWorkbook(fileName, is);
int sheetNum = workbook.getNumberOfSheets();// 得到sheet的个数
for (int i = 0; i < sheetNum; i++) {
Sheet sheet = workbook.getSheetAt(i);
if (!ExcelCommonUtil.isEmptySheet(sheet)) {// sheet存在有效数据
Row row = null;
boolean flag = true;
for (int m = 0; m < CHECK_SHEET_ROWNUM; m++) {
row = sheet.getRow(m);
if (m == 0) {// 验证标题是否有效
if (!isCorrectTitle(row, titleList)) {
break;
}
} else {// 判断row是否为空
flag = flag && ExcelCommonUtil.isRowEmpty(row);
}
}
if (flag) {
errorSheetNames.add(sheet.getSheetName());
} else {
correctSheetNames.add(sheet.getSheetName());
}
}
}
// 不合法的sheet工作空间名称
resultMap.put("errorSheetNames", errorSheetNames);
// 合法的sheet工作空间名称
resultMap.put("correctSheetNames", correctSheetNames);
return resultMap;
}
/**
* 检查是否是正确的标题行
*
* @param row
* @param titleList
* @return
*/
public static boolean isCorrectTitle(Row row, List<String> titleList) throws Exception {
boolean flag = false;
if (!ExcelCommonUtil.isRowEmpty(row)) {
for (int i = row.getFirstCellNum(), len = row.getLastCellNum(); i <= len; i++) {
Cell cell = row.getCell(i);
if (cell != null && cell.getCellType() != Cell.CELL_TYPE_BLANK) {
String cellValue = ExcelCommonUtil.getValue(cell);
if (!titleList.contains(cellValue)) {
flag = false;
break;
} else {
flag = true;
}
}
}
}
return flag;
}
/**
* 获取上传EXCEL的信息
*
* @param uploadfile
* @param tiJianShenQing
* @param sheetNames
* @param jiGouBianMa
* @return
*/
@SuppressWarnings("unchecked")
public static Map<String, Object> getExcelInfo(MultipartFile uploadfile, TShenQingEntity paramTShenQing,
String sheetNames) throws Exception {
Map<String, Object> resultMap = new HashMap<>();// 结果map
List<TShenQingEntity> tShenQingList = null;// 申请list
List<String> errorList = null; // EXCEL文件中存在错误的list
Boolean globalErrorFlag = null;// EXCEL文件中是否出现了不符合要求格式的数据
int total = 0;// 导入项总数
if (!StringUtils.isBlank(sheetNames)) {
List<String> sheetList = StringUtil.StringToList(sheetNames);
Map<String, Object> rowMap = ExcelCommonUtil.getExcelRead(uploadfile.getOriginalFilename(),
uploadfile.getInputStream(), 1, sheetList);
if (rowMap.size() != 0) {
// 查询条件
String conditon = paramTShenQing.getCondition();
// 得到必填字段
List<String> biTianList = ExcelDownloadUtil.listBiTianField(conditon);
boolean biTianValidFlag = (biTianList != null && biTianList.size() != 0);
tShenQingList = new ArrayList<>();
errorList = new ArrayList<>();
TShenQingEntity tShenQing = null;
TUser tUser = null;
// 存放错误提示
StringBuilder tips;
for (Map.Entry<String, Object> entry : rowMap.entrySet()) {
Map<String, Object> tempMap = (Map<String, Object>)entry.getValue();
String fenZuName = "[" + entry.getKey() + "]";// 得到sheet工作表空间名称,方便用于提示
List<String> titleList = (List<String>)tempMap.get("titleList");
List<Row> rowList = (List<Row>)tempMap.get("rowList");
for (int m = 0, lenx = rowList.size(); m < lenx; m++) {
tShenQing = new TShenQingEntity();
tUser = new TUser();
tips = null;
boolean rowFlag = true;// 行数据合法标识
Row dataRow = rowList.get(m);
boolean emptyRowFlag = ExcelCommonUtil.isRowEmpty(dataRow);
total = emptyRowFlag ? total : total + 1;// 如果是空行,直接+1进行下一行的数据解析
for (int i = 0, len = titleList.size(); i < len && !emptyRowFlag; i++) {
String title = titleList.get(i);
String value = ExcelCommonUtil.getValue(dataRow.getCell(i));
if (biTianValidFlag && biTianList.contains(title) && StringUtils.isBlank(value)) {// 如果必填项没有值直接进行下一次循环
tips = tips == null ? new StringBuilder(fenZuName + "第" + (m + 1) + "行的数据:" + title + "未填")
: tips.append("、" + title + "未填");
rowFlag = false;
}
if (!StringUtils.isBlank(value)) {// 如果不为空
FieldEnum tempEnum = FieldEnum.getFieldEnum(title);
Field tempField = null;
if ("TR".equals(tempEnum.getType())) {
for (int n = 0; n < T_USER_FIELD.length; n++) {
tempField = T_USER_FIELD[n];
if (tempEnum.getEntityFieldName().equals(tempField.getName())) {
tempField.setAccessible(true);
if ("出生日期".equals(tempEnum.getName())) {
value = value.contains("-") ? value.replaceAll("-", "/") : (value.contains(".") ? value.replaceAll("\.", "/") : value);
try {
tempField.set(tUser,
new SimpleDateFormat("yyyy/MM/dd").parse(value));
} catch (Exception e) {// 出生日期转换异常
rowFlag = false;
tips = tips == null ? new StringBuilder( fenZuName + "第" + (m + 1) + "行的数据:" + title + "格式有误")
: tips.append("、" + title + "格式有误");
}
} else {
// 这里直接调用set赋值,如果你setter方法里面有处理逻辑的话,可以通过反射调用字段的setter方法赋值
tempField.set(tUser, value);
}
break;
}
}
} else if ("TS".equals(tempEnum.getType())) {
for (int n = 0; n < T_SHEN_QING_FIELD.length; n++) {
tempField = T_SHEN_QING_FIELD[n];
if (tempEnum.getEntityFieldName().equals(tempField.getName())) {
tempField.setAccessible(true);
if ("年龄".equals(tempEnum.getName())) {
try {
value = value.endsWith("岁") ? value.substring(0, value.length() - 1) : value;
tempField.set(tShenQing, Integer.parseInt(value));
} catch (Exception e) {// 年龄转换异常
rowFlag = false;
tips = tips == null ? new StringBuilder( fenZuName + "第" + (m + 1) + "行的数据:" + title + "格式有误(整数)")
: tips.append("、" + title + "格式有误(整数)");
}
} else {
// 这里直接调用set赋值,如果你setter方法里面有处理逻辑的话,可以通过反射调用字段的setter方法赋值
tempField.set(tShenQing, value);
}
break;
}
}
} else {
// doSomthing
}
}
}
// 赋值成功以后也可以进行其他的自定义验证:比如
// 如果姓名为空的话
if (StringUtils.isBlank(tUser.getXingMing())) {
rowFlag = false;
tips = tips == null ? new StringBuilder(fenZuName + "第" + (m + 1) + "行的数据:姓名为空")
: tips.append("、姓名为空");
}
// 其他需要的验证
if (rowFlag) {// 通过验证行合法标识依然是true的话
tShenQing.setUser(tUser);// 把user对象设置进申请对象
tShenQingList.add(tShenQing);// 添加到合法列表中
} else {
globalErrorFlag = globalErrorFlag == null ? Boolean.TRUE : globalErrorFlag;
errorList.add(tips.toString());
}
}
} // end of rowMap
}
}
resultMap.put("tShenQingList", tShenQingList);
resultMap.put("errorList", errorList);
resultMap.put("errorFlag", globalErrorFlag);
resultMap.put("total", total);
return resultMap;
}
}
6.Excel通用工具类:
public class ExcelCommonUtil {
/**
* 判断是否是2003的excel,返回true是2003
*
* @param fileName
* @return
*/
public static boolean isExcel2003(String fileName) {
return fileName.matches("^.+\.(?i)(xls)$");
}
/**
* 判断是否是2007的excel,返回true是2007
*
* @param fileName
* @return
*/
public static boolean isExcel2007(String fileName) {
return fileName.matches("^.+\.(?i)(xlsx)$");
}
/**
* 判断文件是否是Excel文件
*
* @param fileName
* @return
*/
public static boolean validateExcel(String fileName) {
if (fileName == null || !(isExcel2003(fileName) || isExcel2007(fileName))) {
return false;
}
return true;
}
/**
* 得到workbook
*
* @param fileName
* @param is
* @return
* @throws Exception
*/
public static Workbook getWorkbook(String fileName, InputStream is) throws Exception {
Workbook workbook = null;
try {
/** 判断文件的类型,是2003还是2007 */
boolean isExcel2003 = true;
if (isExcel2007(fileName)) {
isExcel2003 = false;
}
if (isExcel2003) {
workbook = new HSSFWorkbook(is);
} else {
workbook = new XSSFWorkbook(is);
}
} catch (Exception e) {
throw e;
}
return workbook;
}
/**
* 得到Cell的值
*
* @param cell
* @return
*/
public static String getValue(Cell cell) throws Exception {
if (cell != null) {
if (cell.getCellType() == Cell.CELL_TYPE_BOOLEAN) {
return String.valueOf(cell.getBooleanCellValue());
} else if (cell.getCellType() == Cell.CELL_TYPE_NUMERIC) {
if (HSSFDateUtil.isCellDateFormatted(cell)) {
Date date = HSSFDateUtil.getJavaDate(cell.getNumericCellValue());
return new SimpleDateFormat("yyyy-MM-dd").format(date);
} else {
double value = cell.getNumericCellValue();
return new BigDecimal(value).toString();
}
} else if (cell.getCellType() == Cell.CELL_TYPE_STRING) {
String value = String.valueOf(cell.getStringCellValue());
Pattern pattern = Pattern.compile("[0-9]{4}.[0-9]{1,2}.[0-9]{1,2}");
if (pattern.matcher(value).matches()) {
return value.replace(".", "-");
} else {
return value;
}
} else {
return String.valueOf(cell.getStringCellValue());
}
}
return "";
}
/**
* 判断一个sheet是否有数据
*
* @param sheet
* @return 空的sheet返回true
*/
public static boolean isEmptySheet(Sheet sheet) {
boolean isEmpty = false;
if ((sheet.getLastRowNum() == 0
&& (sheet.getPhysicalNumberOfRows() == 0 || sheet.getPhysicalNumberOfRows() == 1))) {
isEmpty = true;
}
return isEmpty;
}
/**
* 判断一行是否为空
*
* @param row
* @return
*/
public static boolean isRowEmpty(Row row) {
boolean isRowEmpty = true;
if (row != null) {
for (int i = row.getFirstCellNum(), len = row.getLastCellNum(); i <= len; i++) {
Cell cell = row.getCell(i);
if (cell != null && cell.getCellType() != Cell.CELL_TYPE_BLANK && !"".equals((cell + "").trim())) {
isRowEmpty = false;
}
}
}
return isRowEmpty;
}
/**
* 解析文件的sheet数据
*
* @param fileName 文件名
* @param is 流
* @param startNum 开始行数(标题行)
* @param sheetNameList 工作表空间名称为空时获取所有内容,不过空时获取指定内容
* @return
* @throws Exception
*/
public static Map<String, Object> getExcelRead(String fileName, InputStream is, int startNum, List<String> sheetNameList) throws Exception {
Map<String, Object> map = new HashMap<>();
// 判断其兼容版本 调用了判断版本的方法
Workbook workbook = getWorkbook(fileName, is);
int sheetNum = workbook.getNumberOfSheets();// 得到sheet的个数
List<Row> rowList = null;
List<String> titleList = null;
Map<String, Object> rowMap = null;
boolean isEmptySheetNameList = (sheetNameList == null || sheetNameList.size() == 0);
for (int n = 0; n < sheetNum; n++) {
rowList = new ArrayList<Row>();
titleList = new ArrayList<>();
rowMap = new HashMap<>();
Sheet sheet = workbook.getSheetAt(n);
String sheetName = sheet.getSheetName();
if ((isEmptySheetNameList || sheetNameList.contains(sheetName)) && !isEmptySheet(sheet)) {// sheetNames存在时要满足在此list集合中取值
Row row = null;
for (int i = sheet.getFirstRowNum(), len = sheet.getLastRowNum(); i <= len; i++) {
row = sheet.getRow(i);
if (i == startNum - 1) {
titleList = getRowValue(row);
}
if (i >= startNum) {
if (!isRowEmpty(row)) {
rowList.add(row);
}
}
}
rowMap.put("titleList", titleList);
rowMap.put("rowList", rowList);
map.put(sheetName, rowMap);
}
}
is.close();
return map;
}
/**
* 获取一行的数据
*
* @param row
* @return
*/
private static List<String> getRowValue(Row row) throws Exception {
List<String> list = null;
if (!isRowEmpty(row)) {
list = new ArrayList<>();
for (int i = row.getFirstCellNum(); i <= row.getLastCellNum(); i++) {
Cell cell = row.getCell(i);
if (cell != null && cell.getCellType() != Cell.CELL_TYPE_BLANK) {
String cellValue = ExcelCommonUtil.getValue(cell);
list.add(cellValue);
}
}
}
return list;
}
}
到这里Excel文件导入就完成了,在指定的字段范围内可以实现动态导入啦。