• 若依使用EasyExcel导入和导出数据


    pom

    一定要排除这几个依赖,不然会版本冲突,若依在common模块引入了poi依赖

            <!--        easyexcel-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>easyexcel</artifactId>
                <version>2.2.6</version>
                <exclusions>
                    <exclusion>
                        <artifactId>poi-ooxml-schemas</artifactId>
                        <groupId>org.apache.poi</groupId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.poi</groupId>
                        <artifactId>poi-ooxml</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.apache.poi</groupId>
                        <artifactId>poi</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    实体类

    /**
     * @author:lzp
     * @create: 2022-08-01 15:06
     * @Description: 电力增强类
     */
    
    @Data
    public class SysMonitorExcelDto implements Serializable {
        private static final long serialVersionUID = 1L;
    
        /** 订单id */
        @ExcelValid(message = "编号必须指定")
        @ExcelProperty(value = "编号" ,index = 0)
        private Long id;
    
        /** 用电量 */
        @ExcelProperty(value = "用电量" ,index = 1)
        private Long totalElectricity;
    
        /** 结束时间 */
        @ExcelProperty(value = "结束时间" ,index = 2)
        @DateTimeFormat("yyyy-MM-dd HH:mm:ss")
        private Date endTime;
    
        /** 开始时间 */
        @ExcelProperty(value = "开始时间" ,index = 3)
        @DateTimeFormat("yyyy-MM-dd HH:mm:ss")
        private Date beginTime;
    
        /** 用户编号 */
        @ExcelProperty(value = "用户编号" ,index = 4)
        private Long usrId;
    
        /** 总价 */
        @ExcelProperty(value = "总价" ,index = 5)
        private Long totalMoney;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    1.导出

    1.cmtroller

    调用service方法,完成导出

        /**
         * 导出电力监控列表
         */
        @PreAuthorize("@ss.hasPermi('system:monitor:export')")
        @Log(title = "电力监控", businessType = BusinessType.EXPORT)
        @PostMapping("/export")
        public void export(HttpServletResponse response, SysMonitor sysMonitor) throws IOException {
            sysMonitorService.export(response);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2.service
    调用工具类的方法完成导出
    传入response,excel的标题名称,要导出的数据列表,excel文件名称

        @Override
        public void export(HttpServletResponse response) throws IOException {
            //获取到所有数据 准备导出
            List<SysMonitorExcelDto> list = this.getSysMonitorList();
            //导出
            ExportExcelUtil.exportExcel(response,new MonthSheetWriteHandler("电力监控表"),list,"电力表");
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3.工具类中的方法

    /**
     * @author:lzp
     * @create: 2022-08-02 13:28
     * @Description: excel导出工具类
     */
    
    public class ExportExcelUtil {
        /**
         * @Author lzp
         * @Description:
         * @Date: 13:32 2022/8/2
         * @Param: [response 响应, sheetWriteHandler 控制标题样式, list 要导出的数据, fileName 文件名]
         * @return: void
         */
        public static void exportExcel(HttpServletResponse response,
                                SheetWriteHandler sheetWriteHandler, List<?> list, String fileName) throws IOException {
            response.setContentType("application/vnd.ms-excel");//格式excel
            response.setCharacterEncoding("utf-8");
            //这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
            fileName = URLEncoder.encode(fileName, "UTF-8");
            //Content-disposition:以下载的方式执行此操作
            response.setHeader("Content-disposition", "attachment;filename="+ fileName + ".xlsx");
            //内容样式策略
            WriteCellStyle writeCellStyle = new WriteCellStyle();
            //垂直居中 水平居中
            writeCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
            writeCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
            writeCellStyle.setBorderLeft(BorderStyle.THIN);
            writeCellStyle.setBorderTop(BorderStyle.THIN);
            writeCellStyle.setBorderRight(BorderStyle.THIN);
            writeCellStyle.setBorderBottom(BorderStyle.THIN);
            //设置自动换行 这里不设置,需要自适应宽度
            //writeCellStyle.setWrapped(true);
            //字体策略
            WriteFont writeFont = new WriteFont();
            writeFont.setFontHeightInPoints((short)12);
            writeCellStyle.setWriteFont(writeFont);
            //头策略采用默认
            WriteCellStyle headWriteStyle = new WriteCellStyle();
    
            EasyExcel.write(response.getOutputStream(), SysMonitorExcelDto.class)
                    //设置输出excel,不设置默认为xlsx
                    //.excelType(ExcelTypeEnum.XLS)
                    //设置拦截器自定义样式
                    //宽度自适应
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                    //设置标题(小标题)行高和内容行高
                    .registerWriteHandler(new SimpleRowHeightStyleStrategy((short)40,(short)30))
                    //自定义文件的标题(大标题)这个handler为调用方法时传入的自定义handler
                    .registerWriteHandler(sheetWriteHandler)
                    .registerWriteHandler(new HorizontalCellStyleStrategy(headWriteStyle,writeCellStyle))
                    .sheet(fileName)
                    //设置默认样式及写入头信息开始的行数 也就是每一列的标题 这里是在第四行
                    .useDefaultStyle(true).relativeHeadRowIndex(3)
                    .doWrite(list);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    4.控制标题类
    这个类控制数据之前的显示内容

    /**
     * @author:lzp
     * @create: 2022-08-01 10:51
     * @Description: excel拦截器
     */
    
    
    public class MonthSheetWriteHandler implements SheetWriteHandler {
        private String title;
    	//excel的标题内容 构造方法传入
        public MonthSheetWriteHandler(String title){
            this.title=title;
        }
        @Override
        public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
    
        }
        @Override
        public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
            Workbook workbook = writeWorkbookHolder.getWorkbook();
            Sheet sheet = workbook.getSheetAt(0);
            //第一行
            Row row1 = sheet.createRow(0);
            row1.setHeight((short) 500);
            Cell cell = row1.createCell(0);
            //设置单元格内容
            cell.setCellValue("附件2");
            //第二行
            // 设置标题
            Row row2 = sheet.createRow(1);
            row2.setHeight((short)800);
            Cell cell1 = row2.createCell(0);
            cell1.setCellValue(this.title);
            CellStyle cellStyle = workbook.createCellStyle();
            cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
            cellStyle.setAlignment(HorizontalAlignment.CENTER);
            Font font = workbook.createFont();
            font.setBold(true);
            font.setFontHeight((short)400);
            cellStyle.setFont(font);
            cell1.setCellStyle(cellStyle);
            //调整标题的居中显示位置
            sheet.addMergedRegionUnsafe(new CellRangeAddress(1,1,0,17));
            //第三行
            //设置表日期 填报人 联系方式
            Row row3 = sheet.createRow(2);
            row3.setHeight((short) 500);
            CellStyle cellStyle1=workbook.createCellStyle();
            cellStyle1.setVerticalAlignment(VerticalAlignment.CENTER);
            cellStyle1.setAlignment(HorizontalAlignment.CENTER);
            Cell cell2=row3.createCell(1);
            cell2.setCellStyle(cellStyle1);
            cell2.setCellValue("填表日期");
    
            Cell cell3=row3.createCell(11);
            cell3.setCellStyle(cellStyle1);
            cell3.setCellValue("填报人");
    
            Cell cell4=row3.createCell(15);
            cell4.setCellStyle(cellStyle1);
            cell4.setCellValue("联系方式");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    效果
    在这里插入图片描述

    2.导入

    创建一个listener,逐条插入数据
    1.controller

        @Log(title = "电力管理", businessType = BusinessType.IMPORT)
        @PreAuthorize("@ss.hasPermi('system:monitor:import')")
        @PostMapping("/importData")
        public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception
        {
            try {
                //headRowNumber 4 代表从第五行开始导入
                EasyExcel.read(file.getInputStream(),SysMonitorExcelDto.class,
                        new MyExcelListener(sysMonitorServiceImpl)).headRowNumber(4).sheet().doRead();
            } catch (IOException e) {
                e.printStackTrace();
            }catch (ExcelAnalysisException e){
                return AjaxResult.error(e.getMessage());
            }
            return AjaxResult.success("ok!!");
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2.通用listener

    调用invoke方法,实现数据导入,这里定义通用类型,避免每有一个导入任务就创建一个listener。
    定义一个包含添加的接口,业务实现类实现这个接口,重写添加方法,保证数据准确的添加进数据库。
    创建listener时,传入对应的业务实现类。

    /**
     * @author:lzp
     * @create: 2022-08-02 14:05
     * @Description: 导入数据通用监听器
     */
    
    @Slf4j
    public class MyExcelListener<T> extends AnalysisEventListener<T> {
    
        private Map<String, Object> param;
        //3000条保存一次数据
        private static final int BATCH_COUNT=3000;
        //数据缓存
        private List<T> list = new ArrayList<>(BATCH_COUNT);
        //mapper
        private SaveInterface<T> saveInterface;
    
        //public MyExcelListener(SaveInterface saveInterface, Map param) {
        //    this.saveInterface = saveInterface;
        //    this.param = param;
        //}
    
        public MyExcelListener(SaveInterface<T> saveInterface) {
            this.saveInterface = saveInterface;
        }
    
        @Override
        public void invoke(T data, AnalysisContext analysisContext) {
    
            try {
                //通用方法数据校验
                ExcelImportValid.valid(data);
            }catch (ExceptionCustom e){
                // System.out.println(e.getMessage());
                //在easyExcel监听器中抛出业务异常
                throw new ExcelAnalysisException(e.getMessage());
            }
            log.info("解析到一条数据:{}",data.toString());
            //先将数据加到list中
            list.add(data);
            // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
            if (list.size() >= BATCH_COUNT) {
                saveData();
                // 存储完成清理 list
                list = new ArrayList<>(BATCH_COUNT);
            }
        }
    
        //最后再存储一次数据
        @Override
        public void doAfterAllAnalysed(AnalysisContext analysisContext) {
            // 这里也要保存数据,确保最后遗留的数据也存储到数据库
            saveData();
    
            //logger.info("所有数据解析完成!");
        }
        /**
         * 加上存储数据库
         */
        private void saveData() {
            //logger.info("{}条数据,开始存储数据库!", list.size());
            saveInterface.save(list,param);
            //logger.info("存储数据库成功!");
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    3.接口

    public interface SaveInterface <T>{
        void save(List<T> list, Map<String, Object> param);
    }
    
    • 1
    • 2
    • 3

    4.业务实现类
    实现第三步创建的接口
    在这里插入图片描述
    实现方法
    将dto转为和数据库对应的实体类

        //导入excel数据
        @Override
        public void save(List<SysMonitorExcelDto> list, Map<String, Object> param) {
            for(SysMonitorExcelDto sysMonitorExcelDto:list){
                SysMonitor sysMonitor = new SysMonitor();
                BeanUtils.copyProperties(sysMonitorExcelDto,sysMonitor);
                获取到util.date 开始事件
                Date beginTime = sysMonitorExcelDto.getBeginTime();
                结束时间
                Date endTime = sysMonitorExcelDto.getEndTime();
                sysMonitor.setBegintime(new java.sql.Date(beginTime.getTime()));
                sysMonitor.setEndtime(new java.sql.Date(endTime.getTime()));
                this.insertSysMonitor(sysMonitor);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    3.导入时数据校验

    1.自定义注解

    @Target({ ElementType.FIELD, ElementType.TYPE })
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ExcelValid {
        String message() default "导入有为空得字段";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.校验注解类

    /**
     * @author:lzp
     * @create: 2022-08-02 10:30
     * @Description: excel字段校验类
     */
    
    public class ExcelImportValid {
        /**
         * Excel导入字段校验
         *
         * @param object 校验的JavaBean 其属性须有自定义注解
         */
        public static void valid(Object object) throws ExceptionCustom {
            //获取当前类的所有属性
            Field[] fields = object.getClass().getDeclaredFields();
            for (Field field : fields) {
                //设置可访问
                field.setAccessible(true);
                //属性的值
                Object fieldValue = null;
                try {
                    //获取到字段的值
                    fieldValue = field.get(object);
                } catch (IllegalAccessException e) {
                    throw new ExceptionCustom("IMPORT_PARAM_CHECK_FAIL", "导入参数检查失败");
                }
                //是否包含必填校验注解
                boolean isExcelValid = field.isAnnotationPresent(ExcelValid.class);
                //如果包含这个注解,并且获取到的值为null,抛出异常
                if (isExcelValid && Objects.isNull(fieldValue)) {
                    throw new ExceptionCustom("NULL", field.getAnnotation(ExcelValid.class).message());
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    3.使用方法
    在需要校验的字段加上注解
    在这里插入图片描述
    在invoke中开启校验

            try {
                //通用方法数据校验
                ExcelImportValid.valid(data);
            }catch (ExceptionCustom e){
                // System.out.println(e.getMessage());
                //在easyExcel监听器中抛出业务异常 这个异常会被controller捕获,将错误信息返回给前端
                throw new ExcelAnalysisException(e.getMessage());
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    4.ExceptionCustom 类

    /**
     * @author:lzp
     * @create: 2022-08-02 10:35
     * @Description: 自定义异常
     */
    
    @Data
    @EqualsAndHashCode(callSuper = true)
    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);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
  • 相关阅读:
    LeetCode第七题整数反转
    Chrome的使用技巧
    hypervision理解的记录
    SpringCloud Alibaba——分布式事务 Seata
    Linux性能优化--性能工具:特定进程内存
    李开复:我家的AI是坠吼的
    locust与jmeter测试过程及结果对比
    shell——函数,正则
    开箱即⽤!HashData 云数仓上线华为蓝鲸应⽤商城
    Gin项目实战
  • 原文地址:https://blog.csdn.net/weixin_46666822/article/details/126122621