poi的使用,easypoi,easyexcel的选择

项目需求需要读取excel,接触到poi,使用的话也入了很多坑,自我总结一下

这里呢写了小demo,是一个SpringBoot项目,主要功能就是读取一个总课表,然后生成我们需要的指定班级的课表

先来看看我们的总课表的样子
在这里插入图片描述

easyexcel

对于excel的数据量比较大的时候,速度有着明显优势
关键代码:
导包:

		<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.1.1</version>
        </dependency>
        <!--xls-->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>3.17</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.17</version>
        </dependency>

数据实体类(给需要参与导入导出的属性添加@ExcelProperty注解,其中value属性的值代表要导出的时候的列名,index的值是列的下标,从0开始,这个课表上午和下午中间有间隔,所以为了能准确读取到,这里必须设置):

import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import lombok.experimental.Accessors;

@Data
//@Accessors(chain = true)
public class DemoData {
    //设置excel表头名称
    @ExcelProperty(value = "日期",index = 0)
    private String DateTime;
    @ExcelProperty(value = "星期",index = 1)
    private String week;

    @ExcelProperty(value = "地点",index = 2)
    private String AMClassroom;
    @ExcelProperty(value = "1,2课时",index = 3)
    private String hourOneAndTwo;
    @ExcelProperty(value = "3,4课时",index = 4)
    private String hourThreeAndFour;
    @ExcelProperty(value = "教员",index = 5)
    private String AMTeacher;
    @ExcelProperty(value = "班级",index = 6)
    private String AMGrade;

    @ExcelProperty(value = "地点",index = 8)
    private String PMClassroom;
    @ExcelProperty(value = "5,6课时",index = 9)
    private String hourFiveAndSix;
    @ExcelProperty(value = "7,8课时",index = 10)
    private String hourSevenAndEight;
    @ExcelProperty(value = "教员",index = 11)
    private String PMTeacher;
    @ExcelProperty(value = "班级",index = 12)
    private String PMGrade;

    @ExcelProperty(value = "晚(18:30-20:10)",index = 13)
    private String night;
}

创建excel事件分析器:

import javax.swing.filechooser.FileSystemView;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ExcelListener extends AnalysisEventListener<DemoData> {

    //要打印的班级的课表的集合
    private List<DemoData> list = new ArrayList<>();
    //第一行必保存的标识,是列头
    private boolean flag = true;
    //要生成课表的课表中的班级名称
    public static String grade;
    //表头
    private String head;
    //星期暂存
    private String week;
    //该班级晚自习教室暂存
    private Map<String,String> night=new HashMap<>();

    //读取表头内容
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        //表头,这里课表的表头就一个
        System.out.println("表头:" + headMap);
        head = headMap.get(0).substring(0, headMap.get(0).length() - 3);
    }

    //一行一行读取excel内容
    @Override
    public void invoke(DemoData data, AnalysisContext analysisContext) {
        System.out.println("****" + data);
        //列头的读取
        if ((!StringUtils.isEmpty(data.getWeek())) && flag) {
            list.add(data);
            flag = false;
            return;
        }
        //星期的暂存和数据中星期的赋值
        if (StringUtils.isEmpty(data.getWeek())) {
            data.setWeek(week);
        } else {
            week = data.getWeek();
        }
        //读取这一周的这一天的这个班级的晚自习教室
        if (!StringUtils.isEmpty(data.getNight())) {
            if (data.getNight().contains(grade) || grade.contains(data.getNight())) {
                night.put(data.getWeek(), data.getAMClassroom());
            }
        }
        data.setNight(null);//读取完之后就没有必要存在了,到整体读取完的方法中再整体填充
        //只去找我们需要的班级的课表
        if (grade.equals(data.getAMGrade()) || grade.equals(data.getPMGrade())) {
            //半天半天的区分,如果这一天有这个班级,但是某个半天不是这个班级,就直接清空这半天
            if (!grade.equals(data.getAMGrade())) {
                data.setAMClassroom(null);
                data.setHourOneAndTwo(null);
                data.setHourThreeAndFour(null);
                data.setAMTeacher(null);
                data.setAMGrade(null);
            }
            if (!grade.equals(data.getPMGrade())) {
                data.setPMClassroom(null);
                data.setHourFiveAndSix(null);
                data.setHourSevenAndEight(null);
                data.setPMTeacher(null);
                data.setPMGrade(null);
            }
            //如果和上一条数据的日期相同,那么和上条数据进行合并
            if (list.get(list.size()-1).getWeek().equals(data.getWeek())) {
                DemoData oldData = list.get(list.size() - 1);//合并的目标数据
                //合并到上午
                if (StringUtils.isEmpty(oldData.getAMGrade())) {
                    oldData.setAMClassroom(data.getAMClassroom());
                    oldData.setHourOneAndTwo(data.getHourOneAndTwo());
                    oldData.setHourThreeAndFour(data.getHourThreeAndFour());
                    oldData.setAMTeacher(data.getAMTeacher());
                    oldData.setAMGrade(data.getAMGrade());
                }else {//合并到下午
                    oldData.setPMClassroom(data.getPMClassroom());
                    oldData.setHourFiveAndSix(data.getHourFiveAndSix());
                    oldData.setHourSevenAndEight(data.getHourSevenAndEight());
                    oldData.setPMTeacher(data.getPMTeacher());
                    oldData.setPMGrade(data.getPMGrade());
                }
                list.set(list.size() - 1, oldData);
                return;
            }
            list.add(data);
        }
    }

    //读取完成之后
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        //读完之后开始写入
        // 获取桌面路径
        FileSystemView fsv = FileSystemView.getFileSystemView();
        String desktop = fsv.getHomeDirectory().getPath();
        String filePath = desktop + "\\" + grade + " " + head + ".xls";//C:\Users\fuck\Desktop\T119开9.13-9.18.xls
        //填充晚自习
        for (String wk : night.keySet()) {
            for (DemoData data : list) {
                if (data.getWeek().equals(wk)) {
                    data.setNight(night.get(wk));
                }
            }
        }
        System.out.println(filePath);
        list.forEach(list -> System.out.println(list));

        //写入
        EasyExcel.write(filePath, DemoData.class).sheet("学生列表").doWrite(list);
    }
}

总结excel文件表头的读取,常规行的读取,excel文件的写入都是在这个类中重写的方法中实现,读取数据的特点:一行一行的读取

easyexcel文档地址.

excelpoi

实现方式
导入的依赖改动,使用的注解也改动了
在这里插入图片描述
需要指定列名,和总课表中的列头名字需要一模一样,很难受
而且不知道什么原因,读取不到某些列的数据,使用模板来进行生成的话,如果有一对多的关系,牵扯到单元格的合并,最后导出的表格连打开都报错,最后放弃了,这里就省略了,抱歉

poi

最原生的方式,自由度高,对于这种比较复杂的表格,应该是最合适的选择
导入依赖:

		<!-- poi -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>3.17</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.17</version>
        </dependency>

读取(思路:获取工作表,获取sheet,遍历sheet的row和cell):

	/**
     * 读取总课表
     *
     * @param schedule
     * @param list
     * @throws Exception
     */
    public String timeTableRead(Schedule schedule, List<ScheduleVo> list) throws Exception {
        //读取
        if (!ObjectUtils.isEmpty(schedule.getMultipartFile())) {
            //某一天的课程
            ScheduleVo scheduleVo = new ScheduleVo();
            //文件名
            String fileName = "";

            //MultipartFile 转 inputStream
            InputStream inputStream = schedule.getMultipartFile().getInputStream();
            Workbook sheets = new XSSFWorkbook(inputStream);//创建工作表格
            //获取一个工作表(sheet页),下标从0开始
            Sheet sheet = sheets.getSheetAt(0);
            //遍历行,sheet,row,cell的下标都是从0开始,行数313,sheet.getLastRowNum()也的得到313,所以i可以等于rowNum
            for (int i = 0, rowNum = sheet.getLastRowNum(); i <= rowNum; i++) {
                // 获取行内容
                Row row = sheet.getRow(i);
                if (ObjectUtils.isEmpty(row)) {
                    //如果知识单纯有一行为null就继续下一行的读取
                    continue;
                }
                //防止行缺失列    行!=null,但是读取指定列却空指针,有row缺失cell
                if (row.getPhysicalNumberOfCells() < 13) {
                    continue;
                }
                //读取列
                //读取表头
                if (i == 0) {
                    String title = row.getCell(0).getStringCellValue();
                    fileName = schedule.getName() + " " + title.substring(0, title.length() - 3) + "课表";
                } else if (i > 1) {
                    //第二行列头没有什么好读的,直接第三行开始读取
                    //如果有时间,代表这是新的一天的开始,就把课表对象重写实例化
                    String dateTime = row.getCell(0).getStringCellValue();
                    if (!StringUtils.isEmpty(dateTime)) {
                        if (!StringUtils.isEmpty(scheduleVo.getDateTime())) {//新的一天开始把旧的一天读到的数据添加到集合中
                            list.add(scheduleVo);
                        }
                        scheduleVo = new ScheduleVo();
                        scheduleVo.setDateTime(dateTime.substring(5));//设置时间
                        scheduleVo.setWeek(row.getCell(1).getStringCellValue());//设置星期
                    }
                    //如果第7列是要搜索的班级就把这一行中指定列的数据添加到对象中
                    String clazz = row.getCell(6).getStringCellValue();
                    if (clazz.equals(schedule.getName())) {
                        Cell cell = row.getCell(2);
                        cell.setCellType(CellType.STRING);
                        scheduleVo.setAmClassroom(cell.getStringCellValue());//读取上午的地点
                        scheduleVo.setAmCourse(row.getCell(3).getStringCellValue());//读取上午的课程
                        scheduleVo.setAmTeacher(row.getCell(5).getStringCellValue());//读取上午的教员
                    }
                    //如果第13列是要搜索的班级就把这一行中指定列的数据添加到对象中
                    clazz = row.getCell(12).getStringCellValue();
                    if (clazz.equals(schedule.getName())) {
                        Cell cell = row.getCell(8);
                        cell.setCellType(CellType.STRING);
                        scheduleVo.setPmClassroom(cell.getStringCellValue());//读取下午的地点
                        scheduleVo.setPmCourse(row.getCell(9).getStringCellValue());//读取下午的课程
                        scheduleVo.setPmTeacher(row.getCell(11).getStringCellValue());//读取下午的教员
                    }
                    //如果第14列是要搜索的班级就把这一行中指定列的数据添加到对象中
                    clazz = row.getCell(13).getStringCellValue();
                    if ((!StringUtils.isEmpty(clazz)) && schedule.getName().contains(clazz)) {//这天有晚自习
                        Cell cell = row.getCell(2);
                        cell.setCellType(CellType.STRING);
                        scheduleVo.setNight(cell.getStringCellValue());//保存晚自习地点到晚自习字段中
                    }
                    //如果是最后一行了,读取完之后就直接添加到集合中
                    if (i == rowNum) {
                        list.add(scheduleVo);
                    }
                }
            }
            //关流
            sheets.close();
            inputStream.close();
            return fileName;
        } else {
            throw new Exception("表格为空,请选择表格!");
        }
    }
注意:像这种比较复杂的表格的读取,需要注意,读取到的row对象可能缺少列,正常是这样:


表格中是这样的,本来是隔开周一周二教室安排为了美观:
在这里插入图片描述
但是我遇到的表格不知道表格进行了什么操作,竟然导致缺少cell
在这里插入图片描述
这是遇到的一个坑,不知产生的原因,有知道的大佬欢迎指教。
暂时解决办法判断row的cell数,如果缺斤少两就读取下一行:

	//防止行缺失列    行!=null,但是读取指定列却空指针,有row缺失cell
    if (row.getPhysicalNumberOfCells() < 13) {
          continue;
    }

写入(思路:创建工作表,创建sheet,创建sheet的row,创建row的cell):

    /**
     * 在桌面生成课表
     *
     * @param schedule
     * @param list
     */
    public void timeTableWrite(Schedule schedule, List<ScheduleVo> list) throws IOException {
        //创建工作簿 类似于创建Excel文件
        HSSFWorkbook workbook = new HSSFWorkbook();
        //创建 sheetname页名 直接用的课表集合的时间做一个拼接做页名
        HSSFSheet sheet = workbook.createSheet(list.get(0).getDateTime() + "-" + list.get(list.size() - 1).getDateTime());

        //创建标题行,下标从0开始
        HSSFRow row = sheet.createRow(0);
        //单元格的合并————合并列(合并并不需要创建行或列作为前提)
        CellRangeAddress region = new CellRangeAddress(0, 0, 0, 9);
        sheet.addMergedRegion(region);
        //创建这行中的列,并给该列直接赋值,标题行就只有一列
        row.createCell(0).setCellValue(schedule.getName() + " 课表");
        //但是设置单元格样式需要单元格创建了作为前提,否则报错
        for (int i = 1; i <= 9; i++) {
            row.createCell(i);
        }
        //创建课表标题行的样式
        CellStyle cellStyle = workbook.createCellStyle();
        cellStyle.setAlignment(HorizontalAlignment.CENTER);//水平居中
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);//垂直居中
        cellStyle.setBorderBottom(BorderStyle.THIN);//上下左右一个个设置边框,细线
        cellStyle.setBorderLeft(BorderStyle.THIN);
        cellStyle.setBorderTop(BorderStyle.THIN);
        cellStyle.setBorderRight(BorderStyle.THIN);//单元格的边框颜色默认黑色
        //设置背景颜色
//        cellStyle.setFillForegroundColor(IndexedColors.BLUE1.getIndex());
//        cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);//填充模式,如果不设置会导致背景色上不去
        //给列添加样式,不单单需要第一个单元格需要边框,参与合并的所有单元格都需要
        for (Cell cell : row) {
            cell.setCellStyle(cellStyle);
        }

        //创建列头行
        row = sheet.createRow(1);
        row.createCell(0).setCellValue("日期");
        row.createCell(1).setCellValue("星期");
        row.createCell(2).setCellValue("地点");
        row.createCell(3).setCellValue("上午");
        row.createCell(4).setCellValue("教员");
        row.createCell(5).setCellValue("");
        row.createCell(6).setCellValue("地点");
        row.createCell(7).setCellValue("下午");
        row.createCell(8).setCellValue("教员");
        row.createCell(9).setCellValue("晚自习教室");
        //设置列头样式
        cellStyle = workbook.createCellStyle();
        cellStyle.setAlignment(HorizontalAlignment.CENTER);//水平居中
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);//垂直居中
        cellStyle.setBorderBottom(BorderStyle.THIN);//上下左右一个个设置边框,细线
        cellStyle.setBorderLeft(BorderStyle.THIN);
        cellStyle.setBorderTop(BorderStyle.THIN);
        cellStyle.setBorderRight(BorderStyle.THIN);//单元格的边框颜色默认黑色
        //给列添加样式,不单单需要第一个单元格需要边框,参与合并的所有单元格都需要
        for (Cell cell : row) {
            cell.setCellStyle(cellStyle);
        }

        //开始遍历课表信息集合来创建列
        for (int i = 0; i < list.size(); i++) {
            row = sheet.createRow(i + 2);//标题行为0,列头为1,所以i+2
            row.createCell(0).setCellValue(list.get(i).getDateTime());
            row.createCell(1).setCellValue(list.get(i).getWeek());
            row.createCell(2).setCellValue(list.get(i).getAmClassroom());
            row.createCell(3).setCellValue(list.get(i).getAmCourse());
            row.createCell(4).setCellValue(list.get(i).getAmTeacher());
            //如果是第一次遍历课程集合,就需要合并行,也就是课表中的午休列
            if (i == 0) {
                region = new CellRangeAddress(2, list.size() + 1, 5, 5);
                sheet.addMergedRegion(region);
                row.createCell(5).setCellValue("午休");
            }
            row.createCell(6).setCellValue(list.get(i).getPmClassroom());
            row.createCell(7).setCellValue(list.get(i).getPmCourse());
            row.createCell(8).setCellValue(list.get(i).getPmTeacher());
            row.createCell(9).setCellValue(list.get(i).getNight());
            //添加特殊样式,遍历列,不同的列需要设置不同的样式
            //样式,所有列有的公共样式
            cellStyle = workbook.createCellStyle();
            cellStyle.setAlignment(HorizontalAlignment.CENTER);//水平居中
            cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);//垂直居中
            cellStyle.setBorderBottom(BorderStyle.THIN);//上下左右一个个设置边框,细线
            cellStyle.setBorderLeft(BorderStyle.THIN);
            cellStyle.setBorderTop(BorderStyle.THIN);
            cellStyle.setBorderRight(BorderStyle.THIN);//单元格的边框颜色默认黑色
            for (Cell cell : row) {
                cell.setCellStyle(cellStyle);
            }
        }

        //设定 路径
        // 获取桌面路径
        FileSystemView fsv = FileSystemView.getFileSystemView();
        String desktop = fsv.getHomeDirectory().getPath();
        String filePath = desktop + "/" + fileName + ".xls";
        FileOutputStream stream = new FileOutputStream(new File(filePath));
        // 写入工作表
        workbook.write(stream);
        //关流
        stream.close();
    }
}

最后的效果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值