2022 SpringBoot_easyExcel实现excel文件复杂导入(数据验证)与导出(数据有效性校验)代码实现

easyExcel实现excel文件复杂导入(数据验证)与导出(数据有效性校验)操作

靓仔
最近在做一个项目,用到了excel文件的复杂导入与导出,刚开始确实挺头疼的尤其是数据有效性校验。网上基本找不到方法,后面在easyExcel官网的开发文档中,看到了类似操作,但个人感觉官网讲的不太详细,各位可以看看 easyExcel官网开发文档

https://easyexcel.opensource.alibaba.com/docs/current/quickstart/read

今天闲下来,就写此记录一下。。

数据校验导入实际测试

将excel文档导入,后台将获取数据同时进行数据校验,最终无误后,存入数据库

导入的模板:
在这里插入图片描述

校验不通过:
在这里插入图片描述

校验通过:
在这里插入图片描述

excel文件复杂导入(数据验证)代码实现

引入依赖:

<!--easyexcel-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>easyexcel</artifactId>
        <version>3.1.3</version>
		<exclusions>
			<exclusion>
				<groupId>javax.servlet</groupId>
				<artifactId>servlet-api</artifactId>
			</exclusion>
			<exclusion>
				<groupId>org.apache.poi</groupId>
				<artifactId>poi</artifactId>
			</exclusion>
			<exclusion>
				<groupId>org.apache.poi</groupId>
				<artifactId>poi-ooxml</artifactId>
			</exclusion>
			<exclusion>
				<groupId>org.apache.poi</groupId>
				<artifactId>poi-ooxml-schemas</artifactId>
			</exclusion>
		</exclusions>
    </dependency>

实体类:

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.ContentStyle;
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
import com.alibaba.excel.enums.poi.HorizontalAlignmentEnum;
import lombok.Data;

@Data
@HeadRowHeight(25)//设置行高
public class ConvertstandardExcelDto {

    @ExcelProperty(value = "序号", index = 0)
    private Integer id;

    @ColumnWidth(15)//列宽
    @ExcelProperty(value = "产品分类", index = 1)
    private String productsort;

    @ColumnWidth(40)
    @ExcelProperty(value = "产品名称", index = 2)
    private String productname;

    @ColumnWidth(15)
    @ExcelProperty(value = "长短险标志", index = 3)
    private String longriskflag;

    @ColumnWidth(15)
    @ExcelProperty(value = "交费年期", index = 4)
    private String payyearterm;

    @ExcelProperty(value = "渠道", index = 5)
    @ContentStyle(horizontalAlignment = HorizontalAlignmentEnum.LEFT, dataFormat = 49)
    private String channel;

    @ColumnWidth(15)
    @ExcelProperty(value = "机构", index = 6)
    private String managecom;

    @ColumnWidth(15)
    @ExcelProperty(value = "折算系数", index = 7)
    private String convertstandard;

    @ColumnWidth(20)
    @DateTimeFormat("yyyy-MM-dd")
    @ExcelProperty(value = "规则启用时间", index = 8)
    private String rulestarttime;

    @ColumnWidth(20)
    @ExcelProperty(value = "规则设置日期", index = 9)
    private String rulesetdate;

    @ColumnWidth(15)
    @ExcelProperty(value = "规则设置人", index = 10)
    private String rulesetperson;
}

自定义异常类:

package com.vip.lis.vipConfig.config;

import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * 自定义注解异常
 */
@EqualsAndHashCode(callSuper = true)
@Data
public class ExceptionCustom extends RuntimeException {
    private static final long serialVersionUID = 1L;

    public ExceptionCustom() {
    }

    /**
     * 错误编码
     */
    private String errorCode;

    /**
     * 消息是否为属性文件中的Key
     */
    private boolean propertiesKey = true;

    /**
     * 构造一个基本异常.
     *
     * @param message 信息描述
     */
    public ExceptionCustom(String message) {
        super(message);
    }

    /**
     * 构造一个基本异常.
     *
     * @param errorCode 错误编码
     * @param message   信息描述
     */
    public ExceptionCustom(String errorCode, String message) {
        this(errorCode, message, true);
    }

    /**
     * 构造一个基本异常.
     *
     * @param errorCode 错误编码
     * @param message   信息描述
     */
    public ExceptionCustom(String errorCode, String message, Throwable cause) {
        this(errorCode, message, cause, true);
    }

    /**
     * 构造一个基本异常.
     *
     * @param errorCode     错误编码
     * @param message       信息描述
     * @param propertiesKey 消息是否为属性文件中的Key
     */
    private ExceptionCustom(String errorCode, String message, boolean propertiesKey) {
        super(message);
        this.setErrorCode(errorCode);
        this.setPropertiesKey(propertiesKey);
    }

    /**
     * 构造一个基本异常.
     *
     * @param errorCode 错误编码
     * @param message   信息描述
     */
    public ExceptionCustom(String errorCode, String message, Throwable cause, boolean propertiesKey) {
        super(message, cause);
        this.setErrorCode(errorCode);
        this.setPropertiesKey(propertiesKey);
    }

    /**
     * 构造一个基本异常.
     *
     * @param message 信息描述
     * @param cause   根异常类(可以存入任何异常)
     */
    public ExceptionCustom(String message, Throwable cause) {
        super(message, cause);
    }

}

Controller层:

@RestController
@Slf4j
@RequestMapping(value = "/config")
public class configController {

 @Resource
    private ConvertstandardConfigService convertstandardConfigService;

	/**
     * 产品配置 导入
     */
    @PostMapping("/upLoad")
    public BaseResponse upLoad(@RequestParam("file") MultipartFile file, @RequestParam("operator") String operator) {
    	if (file == null) {
     		// .....报错略过
        }
        if (StringUtils.isBlank(operator)) {
      		// .....报错略过
        }
        String originalFilename = file.getOriginalFilename();
        String s = originalFilename.toLowerCase();
        String s1 = s.substring(s.indexOf('.') + 1).toLowerCase();
        if (!"xlsx".equals(s1) && !"xls".equals(s1)) {
        	// .....报错略过
        }
  		return convertstandardConfigService.importExcel(file, operator);
	}

}




service层:

@Service
@Slf4j
public class ConvertstandardConfigServiceImpl implements ConvertstandardConfigService {

    @Resource
    private ConvertstandardConfigMapper convertstandardConfigMapper;


 /**
     * 产品折标配置 导入
     *
     * @param
     * @return
     */
    @Transactional
    @Override
    public ReturnMessage importExcel(MultipartFile file, String operator) {
        ReturnMessage returnMessage = new ReturnMessage();
        List<ConvertstandardExcelDto> list = new ArrayList<>();
        try {
         log.info("文件开始导入。。。 ");
            //获取excel文件数据并校验
            EasyExcel.read(file.getInputStream(),
                    ConvertstandardExcelDto.class, new ConvertstandardExcelListener(list)).sheet().doRead();

            Date date = DateUtil.parseDate(DateUtil.getCurrentDate(), "yyyy-MM-dd");
            String time = DateUtil.getCurrentDate("HH:mm:ss");

            for (ConvertstandardExcelDto temp : list) {
                //插入数据库
                Convertstandard convertstand = new Convertstandard();
                BeanUtils.copyProperties(temp, convertstand);
                convertstand.setRulestarttime(DateUtil.getDate(temp.getRulestarttime()));
                convertstand.setRulesetdate(DateUtil.getDate(temp.getRulesetdate()));
                if (StringUtils.isBlank(temp.getRulesetdate())) {
                    convertstand.setRulesetdate(DateUtil.parseDate(DateUtil.getCurrentDate(), "yyyy-MM-dd"));
                }
                if (StringUtils.isBlank(temp.getRulesetperson())) {
                    convertstand.setRulesetperson(operator);
                }
                convertstand.setConvertstandardstatus("0");
                convertstand.setMakedate(date);
                convertstand.setMaketime(time);
                convertstand.setOperator(operator);
                convertstand.setModifydate(date);
                convertstand.setModifytime(time);
                convertstand.setModifyoperator(operator);
                convertstandardConfigMapper.insert(convertstand);
            }
            log.info("产品折标配置导入成功。。。");
            returnMessage.setReturnMessage(true, "产品折标配置导入成功");
        } catch (Exception e) {
            log.error("产品折标配置导入出错:" + e.getMessage());
            returnMessage.setReturnMessage(false, "产品折标配置导入出错:" + e.getMessage());
        }
        return returnMessage;
    }

}

easyExcel 核心代码 数据校验Listener

package com.vip.lis.vipConfig.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelAnalysisException;
import com.alibaba.fastjson.JSON;
import com.vip.lis.utils.pubfun.DateUtil;
import com.vip.lis.vipConfig.config.ExceptionCustom;
import com.vip.lis.vipConfig.mapper.ConvertstandardConfigMapper;
import com.vip.lis.vipConfig.pojo.Convertstandard;
import com.vip.lis.vipConfig.pojo.ConvertstandardExcelDto;
import com.vip.lis.vipConfig.pojo.Convertstandardb;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
@Component
public class ConvertstandardExcelListener extends AnalysisEventListener<ConvertstandardExcelDto> {

    List<ConvertstandardExcelDto> res;

    public ConvertstandardExcelListener(List<ConvertstandardExcelDto> res) {
        this.res = res;
    }

    //一行一行读取
    @Override
    public void invoke(ConvertstandardExcelDto temp, AnalysisContext analysisContext) {
        log.info("新的一行开始");
        try {
            //数据校验
            if (StringUtils.isBlank(temp.getProductsort())) {
                throw new ExceptionCustom("1", "产品分类不能为空");
            }
            if (StringUtils.isBlank(temp.getConvertstandard())) {
                throw new ExceptionCustom("1", "折算系数不能为空");
            }
            if (StringUtils.isBlank(temp.getRulestarttime())) {
                throw new ExceptionCustom("1", "规则启用日期不能为空");
            }
            if(temp.getRulestarttime().length() != 10){
                log.info("规则启用日期格式不正确,应为yyyy-mm-dd”或“yyyy/mm/dd格式,请调整");
                throw new ExceptionCustom("1", "规则启用日期格式不正确,应为yyyy-mm-dd”或“yyyy/mm/dd格式,请调整");
            }
            String startTime = temp.getRulestarttime().substring(0, 10);
            String PT4 = "((([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})/(((0[13578]|1[02])/(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)/(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29))";
            String PT5 = "((([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29))";
            Pattern p4 = Pattern.compile(PT4);
            Pattern p5 = Pattern.compile(PT5);
            Matcher matcher = p4.matcher(startTime);
            Matcher matcher1 = p5.matcher(startTime);
            if (!matcher.matches() && !matcher1.matches()) {
                log.info("规则启用日期格式不正确,应为yyyy-mm-dd”或“yyyy/mm/dd格式,请调整");
                throw new ExceptionCustom("1", "规则启用日期格式不正确,应为yyyy-mm-dd”或“yyyy/mm/dd格式,请调整");
            }
//            //规则设置时间
            if (StringUtils.isNotBlank(temp.getRulesetdate())) {
                if(temp.getRulesetdate().length() != 10){
                    log.info("规则设置时间格式不正确,应为yyyy-mm-dd”或“yyyy/mm/dd格式,请调整");
                    throw new ExceptionCustom("1", "规则设置时间格式不正确,应为yyyy-mm-dd”或“yyyy/mm/dd格式,请调整");
                }
                String setTime = temp.getRulesetdate().substring(0, 10);
                matcher = p4.matcher(setTime);
                matcher1 = p5.matcher(setTime);
                if (!matcher.matches() && !matcher1.matches()) {
                    log.info("规则设置时间格式不正确,应为yyyy-mm-dd”或“yyyy/mm/dd格式,请调整");
                    throw new ExceptionCustom("1", "规则设置时间格式不正确,应为yyyy-mm-dd”或“yyyy/mm/dd格式,请调整");
                }
            }

            if (!checkMore(temp, res)) {
                throw new ExceptionCustom("1", "产品分类、产品名称、长短险标记、交费年期、渠道、机构信息组合不可重复");
            }
        } catch (ExceptionCustom e) {
            log.error("数据校验失败:" + e.getMessage());
            //在easyExcel监听器中抛出业务异常
            throw new ExcelAnalysisException(e.getMessage());
        }
        log.info("数据校验通过");
        res.add(temp);
    }


    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        log.info("所有数据解析完成!");
        log.info("解析数据为:{}", JSON.toJSONString(res));
    }

    private boolean checkMore(ConvertstandardExcelDto dto, List<ConvertstandardExcelDto> list) {
        String match = toString(dto);
        for (ConvertstandardExcelDto temp : list) {
            String str = toString(temp);
            if (match.equals(str)) {
                return false;
            }
        }
        return true;
    }

    private String toString(ConvertstandardExcelDto dto) {
        String match = dto.getProductsort().trim();
        if (StringUtils.isNotBlank(dto.getProductname())) {
            match = match + dto.getProductname().trim();
        }
        if (StringUtils.isNotBlank(dto.getLongriskflag())) {
            match = match + dto.getLongriskflag().trim();
        }
        if (StringUtils.isNotBlank(dto.getPayyearterm())) {
            match = match + dto.getPayyearterm().trim();
        }
        if (StringUtils.isNotBlank(dto.getChannel())) {
            match = match + dto.getChannel().trim();
        }
        if (StringUtils.isNotBlank(dto.getManagecom())) {
            match = match + dto.getManagecom().trim();
        }
        return match;
    }


}

导出时数据有效性测试

加了数据有效性校验的数据只能通过下拉框选择,输入其他数据会报错。
在这里插入图片描述
在这里插入图片描述

excel文件复杂导出(数据有效性校验)代码实现

Controller层:

@RestController
@Slf4j
@RequestMapping(value = "/config")
public class configController {


	/**
     * 产品配置 导出
     */
	@GetMapping("/download")
    ResponseEntity<byte[]> exportData(){
        try {
            log.info("********进行 产品折标配置 导出处理********");
            String fileName = "产品折标配置.xlsx";
            QueryWrapper<Convertstandard> queryWrapper = new QueryWrapper<>();
            queryWrapper.orderByAsc("(productsort+0)").orderByAsc("productname").orderByAsc("(payyearterm+0)");
            List<Convertstandard> currentList = convertstandardConfigMapper.selectList(queryWrapper);
            List<ConvertstandardExcelDto> excelList = new ArrayList<>(currentList.size());
            int i = 1;
            for (Convertstandard temp : currentList) {
                ConvertstandardExcelDto excelDto = new ConvertstandardExcelDto();
                excelDto.setId(i++);
                BeanUtils.copyProperties(temp, excelDto);
                if (temp.getRulesetdate() != null) {
                    excelDto.setRulesetdate(DateUtil.getDateStr(temp.getRulesetdate(), "yyyy-MM-dd"));
                }
                if (temp.getRulestarttime() != null) {
                    excelDto.setRulestarttime(DateUtil.getDateStr(temp.getRulestarttime(), "yyyy-MM-dd"));
                }
                excelList.add(excelDto);
            }
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            EasyExcel.write(os, ConvertstandardExcelDto.class).sheet("产品折标配置").registerWriteHandler(new ProductConfigSheetWriteHandler(vipConfigMapper)).doWrite(excelList);
            ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
      
            //创建字节数组
            byte[] bytes = new byte[is.available()];
            //将流读到字节数组中
            is.read(bytes);
            //创建HttpHeaders对象设置响应头信息
            MultiValueMap<String, String> headers = new HttpHeaders();
            //设置要下载方式以及下载文件的名字
            headers.add("Content-Disposition", "attachment;filename=" + fileName);
            //设置响应状态码
            HttpStatus statusCode = HttpStatus.OK;
            //创建ResponseEntity对象
            ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers, statusCode);
            //关闭输入流
            is.close();
            return responseEntity;
        } catch (Exception e) {
            logger.error("下载照片失败", e.getMessage());
            return null;
        }
    }







}

导出数据有效性校验
核心代码handler

package com.vip.lis.vipConfig.handler;

import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.handler.context.SheetWriteHandlerContext;
import com.vip.lis.vipConfig.mapper.CustomerLevelMapper;
import com.vip.lis.vipConfig.pojo.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.DataValidation;

import org.apache.poi.ss.usermodel.DataValidationConstraint;
import org.apache.poi.ss.usermodel.DataValidationHelper;
import org.apache.poi.ss.util.CellRangeAddressList;

import java.util.List;

@Slf4j
public class ProductConfigSheetWriteHandler implements SheetWriteHandler {

    private CustomerLevelMapper vipConfigMapper;

	//mapper这里是拿不到的,需要通过构造器将mapper传入操作
    public ProductConfigSheetWriteHandler(CustomerLevelMapper vipConfigMapper) {
        this.vipConfigMapper = vipConfigMapper;
    }

    @Override
    public void afterSheetCreate(SheetWriteHandlerContext context) {
        try{
            log.info("{}Sheet写入成功。", context.getWriteSheetHolder().getSheetName());

            /**
             * 产品分类数据有效性校验
             */
            List<ProductsortList> productSortList = vipConfigMapper.queryProductsort();
            String[] productSort = new String[productSortList.size()];
            int i = 0;
            for (ProductsortList temp : productSortList) {
                productSort[i++] = temp.getProductsort();
            }
            // 区间设置 比如 第一列第一行和第二行的数据。由于第一行是头,所以第一、二行的数据实际上是第二三行
            // * @param firstRow 开始行 从0开始
            // * @param endRow 结束行  从0开始
            // * @param firstCol   开始列   从0开始
            // * @param endCol  结束列  从0开始
            CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(1, 10000, 1, 1);
            DataValidationHelper helper = context.getWriteSheetHolder().getSheet().getDataValidationHelper();
            DataValidationConstraint constraint = helper.createExplicitListConstraint(productSort);
            DataValidation dataValidation = helper.createValidation(constraint, cellRangeAddressList);
            context.getWriteSheetHolder().getSheet().addValidationData(dataValidation);

            /**
             * 产品名称数据有效性校验
             */
            List<ProductnameList> productNameLists = vipConfigMapper.queryProductname();
            String[] productName = new String[productNameLists.size()];
            i = 0;
            for (ProductnameList temp : productNameLists) {
                int index = temp.getProductname().indexOf("-");
                productName[i++] = temp.getProductname().substring(index + 1);
            }
            cellRangeAddressList = new CellRangeAddressList(1, 10000, 2, 2);
            helper = context.getWriteSheetHolder().getSheet().getDataValidationHelper();
            constraint = helper.createExplicitListConstraint(productName);
            dataValidation = helper.createValidation(constraint, cellRangeAddressList);
            context.getWriteSheetHolder().getSheet().addValidationData(dataValidation);

            /**
             * 长短险标记数据有效性校验
             */
            cellRangeAddressList = new CellRangeAddressList(1, 10000, 3, 3);
            helper = context.getWriteSheetHolder().getSheet().getDataValidationHelper();
            constraint = helper.createExplicitListConstraint(new String[]{"L", "D"});
            dataValidation = helper.createValidation(constraint, cellRangeAddressList);
            context.getWriteSheetHolder().getSheet().addValidationData(dataValidation);

            /**
             * 缴费年期数据有效性校验
             */
            List<PayyeartermList> payYearList = vipConfigMapper.queryPayyearterm();
            String[] payYear = new String[payYearList.size()];
            i = 0;
            for (PayyeartermList temp : payYearList) {
                payYear[i++] = temp.getPayyearterm();
            }
            cellRangeAddressList = new CellRangeAddressList(1, 10000, 4, 4);
            helper = context.getWriteSheetHolder().getSheet().getDataValidationHelper();
            constraint = helper.createExplicitListConstraint(payYear);
            dataValidation = helper.createValidation(constraint, cellRangeAddressList);
            context.getWriteSheetHolder().getSheet().addValidationData(dataValidation);

            /**
             * 渠道数据有效性校验
             */
            List<ChannelList> channelList = vipConfigMapper.queryChannel();
            String[] channel = new String[channelList.size()];
            i = 0;
            for (ChannelList temp : channelList) {
                channel[i++] = temp.getChannelcode();
            }
            cellRangeAddressList = new CellRangeAddressList(1, 10000, 5, 5);
            helper = context.getWriteSheetHolder().getSheet().getDataValidationHelper();
            constraint = helper.createExplicitListConstraint(channel);
            dataValidation = helper.createValidation(constraint, cellRangeAddressList);
            context.getWriteSheetHolder().getSheet().addValidationData(dataValidation);


            /**
             * 机构数据有效性校验
             */
            List<ManagecomList> manageComLists = vipConfigMapper.queryManagecom();
            String[] manageCom = new String[manageComLists.size()];
            i = 0;
            for (ManagecomList temp : manageComLists) {
                manageCom[i++] = temp.getComcode();
            }
            cellRangeAddressList = new CellRangeAddressList(1, 10000, 6, 6);
            helper = context.getWriteSheetHolder().getSheet().getDataValidationHelper();
            constraint = helper.createExplicitListConstraint(manageCom);
            dataValidation = helper.createValidation(constraint, cellRangeAddressList);
            context.getWriteSheetHolder().getSheet().addValidationData(dataValidation);

        }catch (Exception e){
            log.error("产品折标配置数据有效性校验出错:{}",e.getMessage());
            throw e;
        }
    }

}

以上就是全部代码实现步骤,非常感谢各位的观看!!
觉得有帮助的记得一键三连 点赞关注收藏哦!!

让我康康
请添加图片描述
某些人不会看了不点赞吧!!!
请添加图片描述

  • 7
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值