SpringBoot通过WorkBook快速实现对Excel的导入和导出(包括数据校验)

之前转载过一篇对Excel基本操作相关的文章,这篇文章的浏览量迅速飙升,以至于在我博客的热门文章中排到了第三的位置,不过那篇转载的文章实用性差并且讲解不是很清晰,所以打算趁着今天休息,写一篇关于SpringBoot通过WorkBook快速实现对Excel的导入、导出、数据校验的文章,也是便于日后查阅。

1、引入依赖


    org.apache.poi
    poi
    4.0.0


    org.apache.poi
    poi-ooxml
    4.0.0

2、Excel类型枚举

/**
 * @Description:Excel类型枚举
 * @Author:zhangzhixiang
 * @CreateDate:2018/08/31 13:59:48
 * @Version:1.0
 */
public enum ExcelTypeEnum {
    
    /**
     * 报备导入
     */
    REPORT_TYPE(BatchImportConsts.EXCEL_REPORT_MODEL_TYPE, new BatchImportStruBO(BatchImportConsts.excel_report_titles, new ReportRecordCheck(), SpringHelper.getBeanByClass(ReportInsertServiceImpl.class))),
    /**
     * 线索导入
     */
    CLUE_TYPE(BatchImportConsts.EXCEL_CLUE_MODEL_TYPE, new BatchImportStruBO(BatchImportConsts.excel_clue_titles, new ClueRecordCheck(), SpringHelper.getBeanByClass(ClueInsertServiceImpl.class)));
    
    @Setter
    @Getter
    private String type;
    @Setter
    @Getter
    private BatchImportStruBO struBO;

    ExcelTypeEnum(String type, BatchImportStruBO struBO) {
        this.type = type;
        this.struBO = struBO;
    }

    /**
     * 判断key对应的枚举是否存在,若存在,返回此枚举;否则返回null
     *
     * @param key
     * @return
     */
    public static ExcelTypeEnum containKey(String key) {
        ExcelTypeEnum type = null;
        switch (key) {
            case "report":
                type = ExcelTypeEnum.REPORT_TYPE;
                break;
            case "clue":
                type = ExcelTypeEnum.CLUE_TYPE;
                break;
            default:
                type = null;
                break;
        }
        return type;
    }
}

3、 Excel导入相关配置

/**
 * Excel导入相关配置
 *
 * @author zhangzhixiang
 * @data 2018/09/18 11:48:59
 */
public class BatchImportConsts {

    /**
     * Excel导入模式
     */
    public static String EXCEL_CLUE_MODEL_TYPE = "clue";
    public static String EXCEL_REPORT_MODEL_TYPE = "report";
   
    /**
     * 报备批量导入Excel标题映射信息
     */    
    public static List excel_report_titles;
    /**
     * 线索批量导入Excel标题映射信息
     */     
    public static List excel_clue_titles;

    /**
     * 报备导入Excel标题
     */    
    public static final String EXCEL_REPORT_RECORD_COLUMN_PROVINCE = "省";
    public static final String EXCEL_REPORT_RECORD_COLUMN_CITY = "市";
    public static final String EXCEL_REPORT_RECORD_COLUMN_AREA = "区";
    public static final String EXCEL_REPORT_RECORD_COLUMN_CUSTOMER_SOURCE = "客户来源(单位名称)";
    public static final String EXCEL_REPORT_RECORD_COLUMN_ITEM = "项目归属";
    public static final String EXCEL_REPORT_RECORD_COLUMN_DATA_CONTENT_DESC = "数据内容描述";
    public static final String EXCEL_REPORT_RECORD_COLUMN_DATA_NUM = "数据数量";
    public static final String EXCEL_REPORT_RECORD_COLUMN_DATA_PURPOSE = "数据用途";

    /**
     * 线索导入Excel标题
     */
    public static final String EXCEL_CLUE_RECORD_COLUMN_CLUE_SOURCE = "线索来源地";
    public static final String EXCEL_CLUE_RECORD_COLUMN_ITEM = "归属项目";
    public static final String EXCEL_CLUE_RECORD_COLUMN_TAG = "线索标签";
    public static final String EXCEL_CLUE_RECORD_COLUMN_NAME = "线索名称";
    public static final String EXCEL_CLUE_RECORD_COLUMN_DESC = "线索内容";
    public static final String EXCEL_CLUE_RECORD_COLUMN_CLUE_REMARK = "线索备注";
    public static final String EXCEL_CLUE_RECORD_COLUMN_PERSON_NUM = "人数";
    public static final String EXCEL_CLUE_RECORD_COLUMN_PERSON_NAME = "姓名";
    public static final String EXCEL_CLUE_RECORD_COLUMN_ID_CARD = "身份证号";
    public static final String EXCEL_CLUE_RECORD_COLUMN_IPHONE = "手机号";
    public static final String EXCEL_CLUE_RECORD_COLUMN_SEX = "性别";
    public static final String EXCEL_CLUE_RECORD_COLUMN_NATION = "名族";
    
    /**
     * 报备配置(标题头+是否必填)
     */
    static {
        excel_report_titles = new ArrayList<>();
        excel_report_titles.add(new BaseColumn(EXCEL_REPORT_RECORD_COLUMN_PROVINCE, BaseColumn.TRUE));
        excel_report_titles.add(new BaseColumn(EXCEL_REPORT_RECORD_COLUMN_CITY, BaseColumn.FALSE));
        excel_report_titles.add(new BaseColumn(EXCEL_REPORT_RECORD_COLUMN_AREA, BaseColumn.FALSE));
        excel_report_titles.add(new BaseColumn(EXCEL_REPORT_RECORD_COLUMN_CUSTOMER_SOURCE, BaseColumn.TRUE));
        excel_report_titles.add(new BaseColumn(EXCEL_REPORT_RECORD_COLUMN_ITEM, BaseColumn.TRUE));
        excel_report_titles.add(new BaseColumn(EXCEL_REPORT_RECORD_COLUMN_DATA_CONTENT_DESC, BaseColumn.TRUE));
        excel_report_titles.add(new BaseColumn(EXCEL_REPORT_RECORD_COLUMN_DATA_NUM, BaseColumn.FALSE));
        excel_report_titles.add(new BaseColumn(EXCEL_REPORT_RECORD_COLUMN_DATA_PURPOSE, BaseColumn.FALSE));
    }

    /**
     * 线索配置(标题头+是否必填)
     */
    static {
        excel_clue_titles = new ArrayList<>();
        excel_clue_titles.add(new BaseColumn(EXCEL_CLUE_RECORD_COLUMN_CLUE_SOURCE, BaseColumn.FALSE));
        excel_clue_titles.add(new BaseColumn(EXCEL_CLUE_RECORD_COLUMN_ITEM, BaseColumn.FALSE));
        excel_clue_titles.add(new BaseColumn(EXCEL_CLUE_RECORD_COLUMN_TAG, BaseColumn.FALSE));
        excel_clue_titles.add(new BaseColumn(EXCEL_CLUE_RECORD_COLUMN_NAME, BaseColumn.FALSE));
        excel_clue_titles.add(new BaseColumn(EXCEL_CLUE_RECORD_COLUMN_DESC, BaseColumn.FALSE));
        excel_clue_titles.add(new BaseColumn(EXCEL_CLUE_RECORD_COLUMN_CLUE_REMARK, BaseColumn.FALSE));
        excel_clue_titles.add(new BaseColumn(EXCEL_CLUE_RECORD_COLUMN_PERSON_NUM, BaseColumn.FALSE));
        excel_clue_titles.add(new BaseColumn(EXCEL_CLUE_RECORD_COLUMN_PERSON_NAME, BaseColumn.FALSE));
        excel_clue_titles.add(new BaseColumn(EXCEL_CLUE_RECORD_COLUMN_ID_CARD, BaseColumn.TRUE));
        excel_clue_titles.add(new BaseColumn(EXCEL_CLUE_RECORD_COLUMN_IPHONE, BaseColumn.FALSE));
        excel_clue_titles.add(new BaseColumn(EXCEL_CLUE_RECORD_COLUMN_SEX, BaseColumn.FALSE));
        excel_clue_titles.add(new BaseColumn(EXCEL_CLUE_RECORD_COLUMN_NATION, BaseColumn.FALSE));
    }
}

4、批量导入Excel数据结构

/**
 * @Description:批量导入Excel数据结构信息
 * @Author:zhangzhixiang
 * @CreateDate:2018/09/05 19:54:32
 * @Version:1.0
 */
@Data
public class BatchImportStruBO {
    
    /**
     * Excel字段与db字段关系
     */
    private List importTitles;
    /**
     * 记录校验器
     */
    private BaseRecordCheck recordCheck;
    /**
     * 入表处理器
     */
    private BaseInsertService insertHandle;

    public BatchImportStruBO(List importTitles, BaseRecordCheck recordCheck, BaseInsertService insertHandle) {
        this.importTitles = importTitles;
        this.recordCheck = recordCheck;
        this.insertHandle = insertHandle;
    }

}

5、Spring帮助类

/**
 * @Description:Spring帮助类
 * @Author:zhangzhixaing
 * @CreateDate:2018/08/31 16:39:45
 * @Version:1.0
 */
@Component
public class SpringHelper implements ApplicationContextAware {

    private static ApplicationContext applicationContext = null;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if(SpringHelper.applicationContext == null) {
            SpringHelper.applicationContext = applicationContext;
        }
    }

    /**
     * 根据一个bean的id获取配置文件中相应的bean
     */
    public static Object getBean(String beanId) throws BeansException {
        if(applicationContext.containsBean(beanId)) {
            applicationContext.getBean(beanId);
        }
        return null;
    }

    /**
     * 根据一个bean的类型获取配置文件中相应的bean
     */
    public static  T getBeanByClass(Class requiredType) throws BeansException {
        return applicationContext.getBean(requiredType);
    }

    /**
     * 如果BeanFactory包含一个与所给名称匹配的bean的定义,则返回true,否则false
     */
    public static boolean containsBean(String name) {
        return applicationContext.containsBean(name);
    }

    /**
     * 获取Spring容器
     */
    public static ApplicationContext getApplicationContext() {
        return SpringHelper.applicationContext;
    }
}

6、String帮助类

/**
 * @Description:String帮助类
 * @Author:zhangzhixaing
 * @CreateDate:2018/08/31 16:39:45
 * @Version:1.0
 */
public class StringHelper extends StringUtils {

    public StringHelper() {}

    /**
     * 判断是否为空
     */
    public static boolean isBlankAnyWay(String str) {
        return isBlank(str) || "null".equalsIgnoreCase(str);
    }

    /**
     * 判断是否非空
     */
    public static boolean isNotBlankAnyWay(String str) {
        return !isBlankAnyWay(str);
    }

    /**
     * 将null转为空
     */
    public static String nullToEmpty(String str) {
        return Strings.nullToEmpty(str);
    }

    /**
     * 将空转为null
     */
    public static String emptyToNumm(String str) {
        return Strings.emptyToNull(str);
    }

    /**
     * 左填充
     */
    public static String padStart(String str, int minLength, char padChar) {
        return Strings.padStart(str, minLength, padChar);
    }

    /**
     * 右填充
     */
    public static String padEnd(String str, int minLength, char padChar) {
        return Strings.padEnd(str, minLength, padChar);
    }
}

7、Excel单元格

/**
 * @Description:Excel单元格实体
 * @Author:zhangzhixaing
 * @CreateDate:2018/08/31 16:39:45
 * @Version:1.0
 */
@Data
public class BaseColumn {
    
    /**
     * 常量定义
     */
    public static final Boolean TRUE = true;
    public static final Boolean FALSE = false;

    /**
     * Excel中对应的字段名称
     */
    protected String excelName;
    /**
     * Excel字段值
     */
    protected String excelValue;
    /**
     * 是否必填
     */    
    protected Boolean isRequired;
    /**
     * 值得校验是否成功,默认true
     */
    protected boolean checkIsSuccess = true;
    /**
     * 校验结果信息
     */
    protected String checkMessage;
    
    public BaseColumn() {
        super();
    }

    public BaseColumn(String excelName, Boolean isRequired) {
        super();
        this.excelName = excelName;
        this.isRequired = isRequired;
    }

    public BaseColumn(String excelName) {
        super();
        this.excelName = excelName;
    }
}

8、行校验接口

/**
 * @Description:行校验接口
 * @Author:zhangzhixiang
 * @CreateDate:2018/08/31 21:56:32
 * @Version:1.0
 */
public interface BaseRecordCheck {

    /**
     * 记录校验
     *
     * @param cells
     * @return 
     * @throws Exception
     */
    List valueCheck(List cells) throws Exception;

}

9、线索行校验实现

/**
 * @Description:线索行校验
 * @Author:zhangzhixiang
 * @CreateDate:2018/08/31 12:59:48
 * @Version:1.0
 */
public class ClueRecordCheck implements BaseRecordCheck {
    
    @Override
    public List valueCheck(List cells) throws Exception {
        //字段校验
        for (BaseColumn column : cells) {
            switch (column.getExcelName()) {
                case BatchImportConsts.EXCEL_CLUE_RECORD_COLUMN_NAME:
                    new BaseCheck().valueCheck(column);
                    break;
                case BatchImportConsts.EXCEL_CLUE_RECORD_COLUMN_ID_CARD:
                    new ClueIdcardCheck().valueCheck(column);
                    break;
                case BatchImportConsts.EXCEL_CLUE_RECORD_COLUMN_IPHONE:
                    new CluePhoneCheck().valueCheck(column);
                    break;
                case BatchImportCONsts.EXCEL_CLUE_RECORD_COLUMN_SEX:
                    new ClueSexCheck().valueCheck(column);
                    break;
                default:
                    break;
            }
        }
        return cells;
    }
}

10、列校验接口

/**
 * @Description:列校验接口
 * @Author:zhangzhixiang
 * @CreateDate:2018/08/31 21:56:34
 * @Version:1.0
 */
public interface BaseColumnCheck {
    
    /**
     * 校验字段值
     * 
     * @param cell
     * @return
     * @throws Exception
     */
    BaseColumn valueCheck(BaseColumn cell) throws Exception;

    /**
     * 多字段联合校验
     * 
     * @param cells
     * @return
     * @throws Exception
     */
    List valueCheck(List cells) throws Exception;

}

11、线索列校验实现(基础非空校验)

/**
 * @Description:基础非空校验
 * @Author:zhangzhixiang
 * @CreateDate:2018/08/31 21:59:43
 * @Version:1.0
 */
public class BaseCheck implements BaseColumnCheck {

    private static final String EMPTY_MESSAGE = "不能为空;"

    @Override
    public BaseColumn valueCheck(BaseColumn cell) throws Exception {
        if (StringUtils.isBlank(cell.getExcelValue())) {
            cell.setCheckIsSuccess(false);
            cell.setCheckMessage((cell.getCheckMessage() == null ? "" : cell.getCheckMessage()) + cell.getExcelName() + EMPTY_MESSAGE);
        } else {
            cell.setCheckIsSuccess(true);
        }
        return cell;
    }

    @Override
    public List valueCheck(List cells) throws Exception {
        return null;
    }
}

12、线索列校验实现(身份证号校验)

/**
 * @Description:身份证号校验
 * @Author:zhangzhixiang
 * @CreateDate:2018/08/31 21:59:43
 * @Version:1.0
 */
public class ClueIdcardCheck implements BaseColumnCheck {

    private static final String EMPTY_MESSAGE = "不能为空;";

    private static final String WRONG_MESSAGE = "格式不对;";

    @Override
    public BaseColumn valueCheck(BaseColumn cell) throws Exception {
        if(cell.getIsRequired() || (!cell.getIsRequired() && StringHelper.isNotBlankAnyWay(cell.getExcelValue()))) {
            cell.setCheckIsSuccess(false);
            cell.setCheckMessage((cell.getCheckMessage() == null ? "" : cell.getCheckMessage()) + cell.getExcelName() + EMPTY_MESSAGE);
        } else {
            //校验身份证号合法性
            String reg = "^\\d{15}$|^\\d{17}[0-9Xx]$";
            if(!cell.getExcelValue().matches(reg)) {
                cell.setCheckIsSuccess(false);
                cell.setCheckMessage((cell.getCheckMessage() == null ? "" : cell.getCheckMessage()) + cell.getExcelName() + WRONG_MESSAGE);
            }
        }
        return cell;
    }

    @Override
    public List valueCheck(List cells) throws Exception {
        return null;
    }
}

13、线索列校验实现(手机号校验)

/**
 * @Description:手机号校验
 * @Author:zhangzhixiang
 * @CreateDate:2018/08/31 21:59:43
 * @Version:1.0
 */
public class CluePhoneCheck implements BaseColumnCheck {

    private static final String EMPTY_MESSAGE = "不能为空;"

    private static final String WRONG_MESSAGE = "格式不对;"

    private Pattern p = Pattern.compile("^((13[0-9])|(15[^4,\\D])|(18[0,5-9]))\\d{8}$");

    @Override
    public BaseColumn valueCheck(BaseColumn cell) throws Exception {
        if(cell.getRequired() || (!cell.getRequired() && StringHelper,isNotBlankAnyWay(cell.getExcelValue()))) {
            cell.setCheckIsSuccess(false);
            cell.setCheckMessage((cell.getCheckMessage() == null ? "" : cell.getCheckMessage()) + cell.getExcelName() + EMPTY_MESSAGE);
        } else {
            //校验手机号合法性
            Matcher m = p.matcher(cell.getExcelValue());
            if(!m.matches()) {
                cell.setCheckIsSuccess(false);
                cell.setCheckMessage((cell.getCheckMessage() == null ? "" : cell.getCheckMessage()) + cell.getExcelName() + WRONG_MESSAGE);
            }
        }
        return cell;
    }

    @Override
    public List valueCheck(List cells) throws Exception {
        return null;
    }
}

14、线索列校验实现(性别校验)

/**
 * @Description:性别校验
 * @Author:zhangzhixiang
 * @CreateDate:2018/08/31 21:59:43
 * @Version:1.0
 */
public class ClueSexCheck implements BaseColumnCheck {

    private static final String EMPTY_MESSAGE = "不能为空;"

    private static final String WRONG_MESSAGE = "格式不对;"

    private String man = "男";

    private String women = "女";

    @Override
    public BaseColumn valueCheck(BaseColumn cell) throws Exception {
        if(cell.getRequired() || (!cell.getRequired() && StringHelper,isNotBlankAnyWay(cell.getExcelValue()))) {
            cell.setCheckIsSuccess(false);
            cell.setCheckMessage((cell.getCheckMessage() == null ? "" : cell.getCheckMessage()) + cell.getExcelName() + EMPTY_MESSAGE);
        } else {
            //校验手机号合法性
            Matcher m = p.matcher(cell.getExcelValue());
            if(!m.matches()) {
                if(!(man.equals(cell.getExcelValue()) || women.equals(cell.getExcelValue()))) {
                    cell.setCheckIsSuccess(false);
                    cell.setCheckMessage((cell.getCheckMessage == null ? "" : cell.getCheckMessage()) + cell.getExcelName() + WRONG_MESSAGE);
                }
            }
        }
        return cell;
    }

    @Override
    public List valueCheck(List cells) throws Exception {
        return null;
    }
}

15、Excel入库接口

/**
 * @Description:Excel入库接口
 * @Author:zhangzhixiang
 * @CreateDate:2018/08/31 21:59:43
 * @Version:1.0
 */
public interface BaseInsertService {

    /**
     * Excel数据入库
     *
     * @param rows Excle数据
     * @param isSuccess
     * @author zhangzhixiang
     * @data 2018/09/19 18:56:43
     */
    void insertDB(List> rows, Boolean isSuccess) throws Exception;
     
}

16、Excel入库实现(线索)

/**
 * @Description:Excel线索入库接口
 * @Author:zhangzhixiang
 * @CreateDate:2018/08/31 21:59:43
 * @Version:1.0
 */
@Service
public class ClueInsertServiceImpl implements BaseInsertService {

    @Autowired
    public ClueInfoDAO clueInfoDAO;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insertDB(List> rows, Boolean isSuccess) throws Exception {
        //线索自定义入库代码
    }

}

17、Excel公共服务接口(OSS)

/**
 * @Description:Excel服务接口(OSS)
 * @Author:zhangzhixiang
 * @CreateDate:2018/08/31 21:59:43
 * @Version:1.0
 */
public interface ExcelOperateService {
    
    /**
     * Excel文件导入
     * @param fileName 原文件名
     * @param code 文件唯一标示
     * @param model Excel类型标识
     * @return 解析结果
     * @throws Exception
     */
    ExcelParseResultBO excelImport(String code, String model) throws Exception;

    /**
     * Excel文件导出
     *
     * @param response
     * @param rows 需要导出的数据
     * @param fileName 文件名
     * @param sheetName sheet名
     * @return void
     * @author zxzhang
     * @date 2019/10/10
     */
    void excelExport(HttpServletResponse response, List> rows, String fileName, String sheetName) throws Exception;
}

18、Excel公共服务实现(OSS)

/**
 * @Description:Excel公共服务实现(OSS)
 * @Author:zhangzhixiang
 * @CreateDate:2018/08/31 21:59:43
 * @Version:1.0
 */
@Service
public class ExcelOperateServiceImpl implements ExcelOperateService {

    private static final Logger logger = LoggerFactory.getLogger(ExcelOperateServiceImpl.class);

    private static final String SERVICE_ERROR_MESSAGE = "批量导入失败";

    /**
     * Excel文件导入
     *
     * @param code OSS文件唯一标示
     * @param model 业务类型(clue、report)
     * @return 解析结果
     * @throws Exception
     */
    @Override
    public ExcelOperateBO excelImport(String code, String model) throws Exception {
        FileClient client = ClientFactory.createClientByType(BootstrapConsts.file_client_type);
        ExcelOperateBO resultBO = null;
        //1、获取Excel数据
        List> excelDatas = null;
        excelDatas = ExcelRead.getSheetDataWithTitle(client.getFileStream(code), code, null);
        String message;
        if(null == excelDatas || excelDatas.size() < SimpleConst.TWO) {
            message = "文件无有效数据";
            resultBO = new ExcelOperateBO();
            resultBO.setFileOK(false);
            resultBO.setMessage(message);
            return resultBO;
        }
        logger.info("记录数量:" + excelDatas.size());

        //2、校验标题合法性
        resultBO = excelTitleCheck(excelDatas.get(0), model);
        if(!resultBO.getFileOK()) {
            return resultBO;
        }

        //3、将Excel中的每列数据与title名称绑定
        List> excelRows = null;
        excelRows = getExcelData(excelDatas, model);
        if(null == excelRows || excelRows.size() == 0) {
            resultBO = new ExcelOperateBO();
            resultBO.setTotalNum(0);
            resultBO.setSuccessNum(0);
            resultBO.setFailNum(0);
            resultBO.setFileOK(true);
            return resultBO;
        }

        //4、Excel数据校验
        List> successRows = new ArrayList<>();
        List> failRows = new ArrayList<>();
        rowsCheck(excelRows, model, successRows, failRows);

        //5、入库
        if(successRows.size() > 0) {
            insertdb(successRows, model, true);
        }
        if(failRows.size() > 0) {
            insertdb(failRows, model, false);
        }
        return resultBO;
    }

    /**
     * Excel文件导出
     *
     * @param response
     * @param rows 导出文件内容(包括标题)
     * @param fileName 导出文件名
     * @param sheetName sheet名称
     * @return 解析结果
     * @throws Exception
     */
    @Override
    public void excelExport(HttpServletResponse response, List> rows, String fileName, String sheetName) throws Exception {
        ExcelWrite.exportExcel(response, rows, fileName, sheetName);
    }

    /**
     * 数据入库
     *
     * @param rows 待写入的数据
     * @param model 业务类型(clue、report)
     * @param isSuccess 是否成功
     */
    private void insertdb(List> rows, String model, Boolean isSuccess) throws Exception {
        BatchImportStruBO struBO = ExcelTypeEnum.containKey(model).getStruBO();
        BaseInsertService insertHandle = struBO.getInsertHandle();
        insertHandle.insertDB(rows, isSuccess);
    }

    /**
     * Excek数据校验
     *
     * @param excelRows 待校验数据
     * @param model 业务类型(clue、report)
     * @param successRows 校验成功的数据
     * @param failRows 校验失败的数据
     */
    private void rowsCheck(List> excelRows, String model, List> successRows, List> failRows) throws Exception {
        BaseRecordCheck recordCheck = ExcelTypeEnum.containKey(model).getStruBO().getRecordCheck();
        List checkedRow = null;
        boolean isSuccessCheck = true;
        for (List rowData : excelRows) {
            checkedRow = recordCheck.valueCheck(rowData);
            if(checkedRow == null || checkedRow.size() == 0) {
                continue;
            }
            for (BaseColumn cell : checkedRow) {
                if (!cell.isCheckIsSuccess()) {
                    isSuccessCheck = cell.isCheckIsSuccess();
                    break;
                }
            }
            if(!isSuccessCheck) {
                failRows.add(checkedRow);
            } else {
                successRows.add(checkedRow);
            }
            isSuccessCheck = true;
        }
    }

    /**
     * 获取Excel有效数据(将title与Excel数据绑定)
     *
     * @param excelDatas Excel数据
     * @return
     * @throws Exception
     */
    private List> getExcelData(List> excelDatas, String model) throws Exception {
        BatchImportStruBO batchImportStruBO = ExcelTypeEnum.containKey(model).getStruBO();
        List titles = batchImportStruBO.getImportTitles();
        excelDatas.remove(0);
        for(int i = 0; i row = valueTrim(excelDatas.get(i));
            excelDatas.set(i, row);
        }
        List> excelRows = excelDataTidy(titles, excelDatas);
        if (null == excelRows) {
            throw new Exception("excelDatas数据转为excelRows数据报错");
        }
        return excelRows;
    }

    /**
     * 将标题和具体的数据组合
     *
     * @param titles 标题信息
     * @param rowList 记录数据,其中记录的字段索引与对应的标题索引一致
     * @return
     */
    private List> excelDataTidy(List titles, List> rowList) {
        List> datas = new ArrayList<>();
        List rowData = null;
        BaseColumn cellData = null;
        List excelRow = null;
        for(int i = 0; i < rowList.size(); i++) {
            rowData = new ArrayList<>();
            excelRow = rowList.get(i);
            for(int j = 0; j < excelRow.size(); j++) {
                String excelCellValue = excelRow.get(j);
                String title = null;
                if(j < titles.size()) {
                    title = titles.get(j).getExcelName();
                } else {
                    break;
                }
                cellData = new BaseColumn();
                cellData.setExcelName(title);
                cellData.setIsRequired(titles.get(j).getIsRequired());
                cellData.setExcelValue(excelCellValue);
                rowData.add(cellData);
            }
            datas.add(rowData);
        }
        return datas;
    }

    /**
     * 校验标题是否合规
     *
     * @param titles 标题信息
     * @param model 业务类型(clue、report)
     * @return
     */
    private ExcelOperateBO excelTitleCheck(List titles, String model) throws Exception {
        BatchImportStruBO batchImportStruBO = ExcelTypeEnum.containKey(model).getStruBO();
        String message = "解析标题报错";
        titles = valueTrim(titles);
        List orderTitles = batchImportStruBO.getImportTitles();
        boolean  titleCheck = titleCheck(titles, orderTitles);
        if (!titleCheck) {
            throw new Exception(message);
        }
        ExcelOperateBO resultBO= new ExcelOperateBO();
        resultBO.setFileOK(true);
        return resultBO;
    }

    /**
     * 校验Excel中标题是否包含所需要的标题
     *
     * @param titles 标题信息
     * @param orderTitles 要求的标题信息
     * @return
     */
    private boolean titleCheck(List titles, List orderTitles) {
        if (null == titles || titles.size() == 0) {
            return false;
        }
        if(null == orderTitles || orderTitles.size() == 0) {
            return false;
        }
        List names = new ArrayList<>();
        for (BaseColumn column : orderTitles) {
            names.add(column.getExcelName());
        }
        for (String name : names) {
            if(!titles.contains(name)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 去除首尾无效空格
     *
     * @param values 要去空格的字符串
     * @return
     */
    private List valueTrim(List values) {
        if(null == values || values.size() == 0) {
            return values;
        }
        for(int i=0; i < values.size(); i++) {
            if(StringUtils.isBlank(values.get(i))) {
                values.set(i, null);
                continue;
            } else {
                values.set(i, values.get(i).trim());
            }
        }
        return values;
    }
}

19、Excel基础操作

/**
 * @Description:Excel基础操作
 * @Author:zhangzhixiang
 * @CreateDate:2018/08/31 21:59:43
 * @Version:1.0
 */
public class ExcelBaseOperate {

    private static final Logger logger = LoggerFactory.getLogger(ExcelBaseOperate.class);

    private static final String SUFFIX_XLS = "xls";
    private static final String SUFFIX_XLSX = "xlsx";

    /**
     * 获取Excel操作类
     *
     * @param inputStream
     * @param name
     * @return
     * @throws Exception
     */
    public static Workbook getWorkbook(InputStream inputStream, String name) throws Exception {
        Workbook wb = null;
        if (name.endsWith(SUFFIX_XLSX)) {
            wb = new XSSFWorkbook(OPCPackage.open(inputStream));
        } else if (name.endsWith(SUFFIX_XLS)) {
            wb = WorkbookFactory.create(inputStream);
        } else {
            String errorMessage = "无法识别的文件类型。文件名称:" + name;
            logger.error(errorMessage);
            throw new Exception(errorMessage);
        }
        return wb;
    }

}

20、Excel读取操作

/**
 * @Description:Excel读取操作
 * @Author:zhangzhixiang
 * @CreateDate:2018/08/31 21:59:43
 * @Version:1.0
 */
public class ExcelRead {

    private static final Logger logger = LoggerFactory.getLogger(ExcelRead.class);

    private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    /**
     * 获取sheet页标题
     *
     * @param inputStream Excel文件流
     * @param excelName Excel文件名
     * @param sheetName sheet页名称。不输入则默认取第一个sheet
     * @return
     */
    public static List getSheetTitle(InputStream inputStream, String excelName, String sheetName) throws Exception {
        Workbook workbook = ExcelBaseOperate.getWorkbook(inputStream, excelName);
        //没有输入sheetname,则默认第一个sheet
        if (StringUtils.isBlank(sheetName)) {
            sheetName = workbook.getSheetAt(0).getSheetName();
        }
        List> sheetData = parseSheet(workbook, sheetName, 0, 0);
        if(null == sheetData || sheetData.size() == 0) {
            return null;
        }
        return sheetData.get(0);
    }

    /**
     * 获取带标题的sheet页数据
     *
     * @param inputStream Excel文件流
     * @param excelName Excel文件名
     * @param sheetName sheet页名称。不输入则默认取第一个sheet
     * @return
     */
    public static List> getSheetDataWithTitle(InputStream inputStream, String excelName, String sheetName) throws Exception {
        Workbook workbook = ExcelBaseOperate.getWorkbook(inputStream, excelName);
        //没有输入sheetname,则默认第一个sheet
        if (StringUtils.isBlank(sheetName)) {
            sheetName = workbook.getSheetAt(0).getSheetName();
        }
        List> sheetData = parseSheet(workbook, sheetName, 0, workbook.getSheet(sheetName).getPhysicalNumberOfRows());
        return sheetData;
    }

    /**
     * 解析sheet页数据
     *
     * @param wb Excel文件
     * @param sheetName sheet页名称
     * @param startIndex 解析的起始row,从0开始,包含此row
     * @param endIndex 解析的终止row,从0开始,包含此row
     * @return 第一层list:row;第二层list:cell
     */
    private static List> parseSheet(Workbook wb, String sheetName, int startIndex, int endIndex) throws Exception {
        String message = null;
        Sheet sheet = wb.getSheet(sheetName);
        if(null == sheet) {
            message = sheetName + ",此sheet页不存在";
            throw new Exception(message);
        }
        int lastRowNum = sheet.getPhysicalNumberOfRows();
        if(startIndex < 0 || endIndex > lastRowNum || startIndex > endIndex) {
            message = "输入的解析row范围错误。startIndex:" + startIndex + ",endIndex:" + endIndex;
            throw new Exception(message);
        }
        List> sheetData = new ArrayList<>();
        int cellCount = -1;
        //row list
        for (int i = startIndex; i < endIndex; i++) {
            Row row = sheet.getRow(i);
            if(null == row) {
                continue;
            }
            List rowData = new ArrayList<>();
            cellCount = row.getLastCellNum();
            //cell list
            for(int j = 0; j < cellCount; j++) {
                Cell cell = row.getCell(j);
                if(null == cell) {
                    rowData.add(null);
                    continue;
                }
                rowData.add(getCellValue(cell));
            }
            sheetData.add(rowData);
        }
        return sheetData;
    }

    /**
     * 获取cell值
     *
     * @param cell
     * @return
     */
    private static String getCellValue(Cell cell) {
        CellType type = cell.getCellTypeEnum();
        String value = null;
        if(CellType.NUMERIC.equals(type)) {
            if(DateUtil.isCellDateFormatted(cell)) {
                Date date = cell.getDateCellValue();
                value = sdf.format(date);
            } else {
                cell.setCellType(CellType.STRING);
                value = String.valueOf(cell.getStringCellValue());
            }
        } else if(CellType.BOOLEAN.equals(type)) {
            value = String.valueOf(cell.getBooleanCellValue());
        } else if(CellType.BLANK.equals(type)) {
            value = null;
        } else {
            value = String.valueOf(cell.getStringCellValue());
        }
        return value;
    }
}

21、Excel写入操作

/**
 * @Description:Excel读取操作
 * @Author:zhangzhixiang
 * @CreateDate:2018/08/31 21:59:43
 * @Version:1.0
 */
public class ExcelWrite {

    private static final Logger logger = LoggerFactory.getLogger(ExcelWrite.class);

    public static final String SUFFIX = "xlsx";

    /**
     * 导出Excel
     *
     * @param
     * @return void
     * @author zhangzhixiang
     * @data 2018/09/10 13:59:43
     */
    public static void exportExcel(HttpServletResponse response, List> rows, String fileName, String sheetName) {
        Workbook workbook = createWb();
        try(OutputStream out = response.getOutputStream()) {
            Sheet sheet = workbook.createSheet(sheetName);
            sheetAppendData(rows, sheet);
            response.setContentType("application/ms-excel;charset=UTF-8");
            response.setHeader("Content-Disposition", "attachment;filename="
                    .concat(String.valueOf(URLEncoder.encode(fileName, "UTF-8"))));
            workbook.write(out);
        } catch(Exception e) {
            logger.error("ExcelWrite--exportExcel throw Exception.fileName:{}", fileName, e);
        }
    }

    /**
     * 生成Excel文件流
     *
     * @param rows
     * @param name
     * @return java.io.ByteArrayOutputStream
     * @author zhangzhixiang
     * @date 2018/09/10 20:54:46
     */
    public static ByteArrayOutputStream writeData(List> rows, String name) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        Workbook workbook = createWb();
        Sheet sheet= workbook.createSheet(name);
        sheetAppendData(rows, sheet);
        try {
            workbook.write(out);
            out.flush();
            return out;
        } catch(Exception e) {
            logger.error("ExcelWrite-->writeData throw Exception.name:{}.", name, e);
            return null;
        }
    }

    /**
     * sheet页填充数据
     *
     * @param rows
     * @param sheet
     * @return org.apache.poi.ss.usermodel.Sheet
     * @author zhangzhixiang
     * @date 2018/09/10 10:59:48
     */
    public static Sheet sheetAppendData(List> rows, Sheet sheet) {
        int lastRowNum = sheet.getPhysicalNumberOfRows();
        if(null == rows || rows.size() == 0) {
            return sheet;
        }
        Row row = null;
        for (List rowData : rows) {
            row = sheet.createRow(lastRowNum);
            rowCreateData(rowData, row);
            lastRowNum = lastRowNum + 1;
        }
        return sheet;
    }

    /**
     * 生成row
     *
     * @param datas
     * @param row
     * @return org.apache.poi.ss.usermodel.Row
     * @author zhangzhixiang
     * @date 2018/09/10 20:35:46
     */
    private static Row rowCreateData(List datas, Row row) {
        Cell cell = null;
        for(int i = 0; i < datas.size(); i++) {
            cell = row.createCell(i);
            cell.setCellType(CellType.STRING);
            cell.setCellValue(datas.get(i));
        }
        return row;
    }

    /**
     * 创建workbook
     *
     * @param
     * @return org.apache.poi.ss.usermodel.Workbook
     * @author zhangzhixiang
     * @date 2018/09/10 20:35:42
     */
    public static Workbook createWb() {
        Workbook wb = null;
        if(SimpleConst.EXCEL_EXT.equals(SUFFIX)) {
            wb = new XSSFWorkbook();
        } else {
            wb = new HSSFWorkbook();
        }
        return wb;
    }
}

22、常量类定义

/**
 * @Description:简单常亮定义
 * @Author:zhangzhixiang
 * @CreateDate:2018/08/31 11:34:56
 * @Version:1.0
 */
public class SimpleConsts {
    
    public static final String EXCEL_EXT = "xlsx";
 
}

23、ExcelOperateBO

/**
 * @Description:Excel操作结果
 * @Author:zhangzhixiang
 * @CreateDate:2018/09/05 19:54:32
 * @Version:1.0
 */
@Data
public class ExcelOperateBO {
    /**
     * 文件是否成功
     */
    private Boolean fileOK;
    /**
     * 失败原因
     */
    private String message;
    /**
     * 总行数
     */
    private Integer totalNum;
    /**
     * 成功行数
     */
    private Integer successNum;
    /**
     * 失败行数
     */
    private Integer failNum;
}

24、controller层

/**
 * @Description:公共服务接口-controller
 * @Author:zhangzhixiang
 * @CreateDate:2018/08/31 11:34:56
 * @Version:1.0
 */
@RestController
@RequestMapping("/api/ops/common")
public class CommonController extends ExceptionHandlerAdvice {
    
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private ExcelOperateService excelOperateService;

    @RequestMapping(value = "/excelImport", method = RequestMethod.POST)
    public ResponseResult excelImport(@RequestBody ExcelImportBO excelImportBO) throw Exception {
        Boolean flag = paramCheckModule(excelImportBO.getModule());
        if(!flag) {
            return new ResponseResult().setSuccess(false).setMessage(ResultCode.ERROR_PARAM_CHECK.getMessage()).setCode(ResultCode.ERROR_PARAM_CHECK.getCode());
        }
        ExcelOperateResultBO data = excelOperateService.excelImport(excelImportBO.getCode());
        return new ResponseResult().setSuccess(true).setData(data).setMessage(ResultCode.SUCCESS.getMessage);
    }

    @RequestMapping(value = "/excelExport", method = RequestMethod.GET)
    public void excelExport(@RequestBody List> rows, String fileName, String sheetName, HttpServletResponse response) throw Exception {
        excelOperateService.excelExport(response, rows, fileName, sheetName);
    }

    private boolean paramCheckModule(String module) throws Exception {
        ExcelTypeEnum type = ExcelTypeEnum.containKey(module);
        if(null == type) {
            logger.error("********************No specified module was found.module:{}**********************", module);
            return false;
        }
        return true;
    }
 
}

全篇文章完全纯手打,如果觉得对您有帮助,记得加关注给好评哟~~

你可能感兴趣的:(Excel,SpringBoot)