EasyExcel的导入导出,改变单元格样式,导出到不同的sheet

本文介绍了如何使用阿里巴巴的EasyExcel库来处理Excel数据,包括添加依赖、创建实体类、定义监听器、实现数据导入和导出功能,以及在监听器中处理数据保存。此外,还展示了如何根据条件给单元格染色,以及将不同类型数据导出到不同sheet中的示例。
摘要由CSDN通过智能技术生成

easyExcel 官方文档https://github.com/alibaba/easyexcel

1.添加依赖

<!--我用的是3.1.1版本-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.1.1</version>
</dependency>
<!--版本由父工程控制-->
<dependency>
    <groupId>org.apache.xmlbeans</groupId>
    <artifactId>xmlbeans</artifactId>
</dependency>

2.创建excel实体类

@ExcelProperty注解 可以双向操作excel,读取excel中的数据和把java程序中的数据写入excel,括号中的值是excel中的标头

默认是匹配excel中的标头,还可以使用index 匹配行号

@Data
public class ExcelDictDTO {
    @ExcelProperty("id")
    private Long id;

    @ExcelProperty("上级id")
    private Long parentId;

    @ExcelProperty("名称")
    private String name;

    @ExcelProperty("值")
    private Integer value;

    @ExcelProperty("编码")
    private String dictCode;
}

3.创建监听器

在创建监听器时可以加入泛型<>,也就是excel实体类,相当于创建专门操作这种excel数据的监听器

@Slf4j
public class ExcelDictDTOListener extends AnalysisEventListener<ExcelDictDTO> {
    //解析excel数据时的操作
    //excel默认是一行一行的解析数据的
    @Override
    public void invoke(ExcelDictDTO data, AnalysisContext analysisContext) {
		log.info("解析到一条数据: {}",data);
        //调用方法save 保存解析到的数据
    }
    //解析excel数据 结束后的操作
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
		log.info("所有数据解析完成!");
    }
}

4.简单导入Excel

在业务类中使用EasyExcel.read()方法,在监听器中保存解析到的数据,就可以实现将excel中的数据保存到数据库

.read()方法有多个重载,可以传入File类型,也可以传入String类型的文件路径Path,也可以传入io流,这里我使用了InputStream 流的方式传入excel数据

@Service
@Slf4j
public class DictServiceImpl extends ServiceImpl<DictMapper, Dict> implements DictService {
    /**
     * 导入数据
     * @param inputStream
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void importData(InputStream inputStream) {
        //使用easyExcel解析上传的excel数据
        EasyExcel.read(inputStream, ExcelDictDTO.class, new ExcelDictDTOListener()).sheet().doRead();
        log.info("Excel导入成功");
    }
}

5.在监听器中加入保存方法

需要注意的有:

在监听器中,应该使用构造方法传入保存数据的对象(mapper或者service),不要忘了加无参构造方法

当导入的数据带id时,批量插入方法尽量不用mybatis-plus自带的方法,我测试虽然插入成功了,但是id是自增的,而不是数据中的id,

如果导入的数据不带id,就无所谓了

原因可能是实体类id属性上有 type = IdType.AUTO(未确认),这里我自己在xml中写了insert语句

批量插入时数据可能不是整数,余下的数据不够触发插入条件,需要最后保存一下

监听器

@Slf4j
@NoArgsConstructor
public class ExcelDictDTOListener extends AnalysisEventListener<ExcelDictDTO> {
    //使用mapper保存数据
    private DictMapper dictMapper;
    //数据list,用与批量插入数据
    private List<ExcelDictDTO> list = new ArrayList<>();
    //测试环境下每5条数据保存一次
    private static final int BEACH_COUNT=5;

    public ExcelDictDTOListener(DictMapper dictMapper) {
        this.dictMapper = dictMapper;
    }

    //
    //解析excel数据时的操作
    //excel默认是一行一行的解析数据的
    @Override
    public void invoke(ExcelDictDTO data, AnalysisContext analysisContext) {
        log.info("解析到一条数据: {}",data);
        //将数据保存在list中
        list.add(data);
        //当数据达到5条时插入一次
        if (list.size()>=BEACH_COUNT){
            saveData(list);
            //插入成功后清空list
            list.clear();
        }
    }

    private void saveData(List<ExcelDictDTO> list) {
        //调用方法保存到数据库
        dictMapper.insertBatch(list);
        log.info("插入 {}条数据",list.size());
    }

    //解析excel数据 结束后的操作
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        log.info("所有数据解析完成!");
        //最后不够5条数据的也保存一下
        saveData(list);
    }
}

批量插入sql

<insert id="insertBatch">
		insert into dict (
		id ,
		parent_id ,
		name ,
		value ,
		dict_code
		) values
		<foreach collection="list" item="item" index="index" separator=",">
			(
			#{item.id} ,
			#{item.parentId} ,
			#{item.name} ,
			#{item.value} ,
			#{item.dictCode}
			)
	    </foreach>
</insert>

controller

@Slf4j
@RestController
@RequestMapping("/admin/core/dict")
public class AdminDictController {
    @Autowired
    private DictService dictService;

    @PostMapping("/import")
    @ApiOperation("Excel批量导入")
    public R beachImport(@RequestParam("file") MultipartFile file) {
        try {
            InputStream inputStream = file.getInputStream();
            dictService.importData(inputStream);
            return R.ok().message("数据批量导入成功");
        } catch (Exception e) {
            log.error("文件上传失败: {}",e.getMessage());
            return R.error().message(ResponseEnum.UPLOAD_ERROR.getMessage());
        }
    }
}

serviceImpl

在这里以有参构造的方式,把baseMapper传入监听器

在方法上加事务注解,出现异常时回滚,防止在导入数据时有异常,导致插入的数据不完整,

@Service
@Slf4j
public class DictServiceImpl extends ServiceImpl<DictMapper, Dict> implements DictService {
    /**
     * 导入数据
     * @param inputStream
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void importData(InputStream inputStream) {
        //使用easyExcel解析上传的excel数据
        EasyExcel.read(inputStream, ExcelDictDTO.class, new ExcelDictDTOListener(this.baseMapper)).sheet().doRead(); //使用有参构造传入baseMapper
        log.info("Excel导入成功");
    }
}

5.简单导出Excel

controller

    @GetMapping("/export")
    public void export(HttpServletResponse response, Dict dictParam) {
        try {
            //设置响应数据类型为excel
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("UTF-8");
            //防止文件名中文乱码
            String fileName = URLEncoder.encode("数据字典", "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
            //要导出的数据
            List<Dict> list = dictService.list();
            List<ExcelDictDTO> dataList = list.stream().map(item -> {
                ExcelDictDTO dto = new ExcelDictDTO();
                BeanUtils.copyProperties(item, dto);
                return dto;
            }).collect(Collectors.toList());
            //ExcelDictDTO.class 可写可不写
            EasyExcel.write(response.getOutputStream(), ExcelDictDTO.class).sheet("数据字典").doWrite(dataList);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

下面提供一些需求案例,所有案例都是将数据导出成excel并下载

案例中所用的实体类

DemoDataDto

@Data
public class DemoDataDto {
    @ExcelProperty("字符串列")
    private String name;
    
    @ExcelProperty("日期列")
    @DateTimeFormat("yyyy年MM月dd日")
    private Date date;

    @ExcelProperty("数字列")
    @NumberFormat("0.00%") //使用百分比格式化数据,使用#.##% 也可以
    private Double doubleNum;

}

DemoDataExport

@Data
public class DemoDataExport {
    @ExcelProperty("字符串列")
    private WriteCellData<String> name;

    @ExcelProperty("日期列")
    @DateTimeFormat("yyyy年MM月dd日")
    private WriteCellData<Date> date;

    @ExcelProperty("数字列")
    @NumberFormat("0.00%") 
    private WriteCellData<Double> doubleNum;
}

需求案例:导出excel时,根据条件给单元格染色

符合条件的染色

主要操作就是把数据转成WriteCellData<>类型,然后通过属性WriteCellStyle来控制样式

@GetMapping("/export")
    public void export(HttpServletResponse response) {
        try {
            //设置响应
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("UTF-8");
            //防止中文乱码
            String fileName = URLEncoder.encode("测试Excel", "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
            //要导出的数据
            List<DemoDataDto> dataList=new ArrayList<>();
            for (int i = 0; i<5; i++){
                DemoDataDto dataDto=new DemoDataDto();
                dataDto.setName("我是字符串"+i);
                dataDto.setDoubleNum(0.33+0.01*i);
                dataDto.setDate(new Date());
                dataList.add(dataDto);
            }
            //要操作样式,需要把数据转换成WriteCellData类型
            List<DemoDataExport> exportList=new ArrayList<>();
            for (DemoDataDto data : dataList) {
                Double num = data.getDoubleNum();
                Date date = data.getDate();
                String name = data.getName();
                WriteCellData<Double> numData=new WriteCellData<>(BigDecimal.valueOf(num));
                WriteCellData<Date> dateData = new WriteCellData<>(date);
                WriteCellData<String> nameData=new WriteCellData<>(name);
                if (name.contains("2")){
                    WriteCellStyle writeCellStyle = new WriteCellStyle();
                    // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.
                    writeCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
                    // 背景蓝色
                    writeCellStyle.setFillForegroundColor(IndexedColors.LIGHT_BLUE.getIndex());
                    nameData.setWriteCellStyle(writeCellStyle);
                }
                if (num==0.37){
                    WriteCellStyle writeCellStyle = new WriteCellStyle();
                    // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.
                    writeCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
                    // 背景绿色
                    writeCellStyle.setFillForegroundColor(IndexedColors.GREEN.getIndex());
                    numData.setWriteCellStyle(writeCellStyle);
                }
                DemoDataExport export=new DemoDataExport();
                export.setDate(dateData);
                export.setName(nameData);
                export.setDoubleNum(numData);
                exportList.add(export);
            }
            EasyExcel.write(response.getOutputStream(), DemoDataExport.class).sheet("测试excel").doWrite(exportList);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

还有很多属性可以设置

 	    WriteCellStyle writeCellStyle = new WriteCellStyle();
	    // 设置字体
        WriteFont contentWriteFont = new WriteFont();
        //设置字体大小
        contentWriteFont.setFontHeightInPoints((short) 10);
        //设置字体名字
        contentWriteFont.setFontName("宋体");
        //在样式用应用设置的字体;
        writeCellStyle.setWriteFont(contentWriteFont);
        //样式  设置底边框;
        writeCellStyle.setBorderBottom(BorderStyle.THIN);
        //设置底边框颜色;
        writeCellStyle.setBottomBorderColor(IndexedColors.GREY_25_PERCENT.getIndex());
        //设置左边框;
        writeCellStyle.setBorderLeft(BorderStyle.THIN);
        //设置左边框颜色;
        writeCellStyle.setLeftBorderColor(IndexedColors.GREY_25_PERCENT.getIndex());
        //设置右边框;
        writeCellStyle.setBorderRight(BorderStyle.THIN);
        //设置右边框颜色;
        writeCellStyle.setRightBorderColor(IndexedColors.GREY_25_PERCENT.getIndex());
        //设置顶边框;
        writeCellStyle.setBorderTop(BorderStyle.THIN);
        ///设置顶边框颜色;
        writeCellStyle.setTopBorderColor(IndexedColors.GREY_25_PERCENT.getIndex());
        // 水平居中
        writeCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        // 垂直居中
        writeCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        //设置自动换行;
        writeCellStyle.setWrapped(true);

需求案例:将相同类型的数据导出到不同的sheet中

在这里插入图片描述在这里插入图片描述

先创建写对象,再创建sheet对象,把数据写到不同的sheet中

@GetMapping("/export2")
    public void export2(HttpServletResponse response) {
        try {
            //设置响应
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("UTF-8");
            //防止中文乱码
            String fileName = URLEncoder.encode("测试Excel2", "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
            //要导出的数据
            List<DemoDataDto> dataList=new ArrayList<>();
            for (int i = 0; i<5; i++){
                DemoDataDto dataDto=new DemoDataDto();
                dataDto.setName("我是字符串"+i);
                dataDto.setDoubleNum(0.33+0.01*i);
                dataDto.setDate(new Date());
                dataList.add(dataDto);
            }
            //创建写对象
            ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream(), DemoDataDto.class).build();
            //sheet对象
            WriteSheet sheet1 = EasyExcel.writerSheet(0, "test").build();
            WriteSheet sheet2 = EasyExcel.writerSheet(1, "test2").build();
            //这里把上面的5条数据写到了两个sheet中
            excelWriter.write(dataList.subList(0, 2),sheet1 ).write(dataList.subList(2,5), sheet2);
            //最后需要调这个方法,否则不会写入excel中
            excelWriter.finish();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

需求案例:将不同类型的数据导出到不同的sheet中

在这里插入图片描述
在这里插入图片描述

和上面差不多,需要注意的只有:

因为导出的数据类型不是同一个实体类,write()方法中就不能加xxx.class了,

在所有sheet都写完才能.finish()

@GetMapping("/export3")
    public void export3(HttpServletResponse response) {
        try {
            //设置响应
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("UTF-8");
            //防止中文乱码
            String fileName = URLEncoder.encode("测试Excel3", "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
            //要导出的数据1  DemoDataDto类型
            List<DemoDataDto> dataList=new ArrayList<>();
            for (int i = 0; i<5; i++){
                DemoDataDto dataDto=new DemoDataDto();
                dataDto.setName("我是字符串"+i);
                dataDto.setDoubleNum(0.33+0.01*i);
                dataDto.setDate(new Date());
                dataList.add(dataDto);
            }
            System.out.println(dataList);
            //要导出的数据2 DemoDataExport类型
            //数据转换成WriteCellData类型
            List<DemoDataExport> exportList=new ArrayList<>();
            for (DemoDataDto data : dataList) {
                Double num = data.getDoubleNum();
                Date date = data.getDate();
                String name = data.getName();
                WriteCellData<Double> numData=new WriteCellData<>(BigDecimal.valueOf(num));
                WriteCellData<Date> dateData = new WriteCellData<>(date);
                WriteCellData<String> nameData=new WriteCellData<>(name);
                nameData.setType(CellDataTypeEnum.STRING);
                if (name.contains("2")){
                    WriteCellStyle writeCellStyle = new WriteCellStyle();
                    writeCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
                    // 背景蓝色
                    writeCellStyle.setFillForegroundColor(IndexedColors.LIGHT_BLUE.getIndex());
                    nameData.setWriteCellStyle(writeCellStyle);
                }
                if (num==0.37){
                    WriteCellStyle writeCellStyle = new WriteCellStyle();
                    writeCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
                    // 背景蓝色
                    writeCellStyle.setFillForegroundColor(IndexedColors.GREEN.getIndex());
                    numData.setWriteCellStyle(writeCellStyle);
                }
                DemoDataExport export=new DemoDataExport();
                export.setDate(dateData);
                export.setName(nameData);
                export.setDoubleNum(numData);
                exportList.add(export);
            }
            ServletOutputStream outputStream = response.getOutputStream();
            //因为导出的数据类型不是同一个实体类,所以这里就不能加xxx.class了
            //ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream(), DemoDataDto.class).build();
            ExcelWriter excelWriter = EasyExcel.write(outputStream).build();
            WriteSheet sheet1 = EasyExcel.writerSheet(0, "test").build();
            excelWriter.write(dataList,sheet1 );
            WriteSheet sheet2 = EasyExcel.writerSheet(1, "test2").build();
            excelWriter.write(exportList ,sheet2 );
            //需要注意的只有在所有sheet都写完了 才能.finsh();
            excelWriter.finish();
            //response.flushBuffer();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

颜色对比

在这里插入图片描述在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值