java实现动态Excle模板文件导入

这里的动态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文件导入就完成了,在指定的字段范围内可以实现动态导入啦。

posted @ 2019-11-27 12:08  三分魔系  阅读(519)  评论(0编辑  收藏  举报