在 SpringBoot 中生成有表头联动筛选功能的 Excel 文件

工具

EasyExcel 和 Apache POI(为什么 POI 能全套实现还要用 EasyExcel ?原因之一是 EasyExcel 采用流式写入的方式,可以有效地管理内存,适用于处理大数据量的场景。相较于 Apache POI 的传统方法,这可以避免在处理大文件时出现内存不足的问题)

效果图

代码

Maven 依赖(其他版本应该也行,并非要使用该特定版本)

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>easyexcel</artifactId>
        <version>3.2.1</version>
    </dependency>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>5.2.3</version>
    </dependency>

POJO

@Data
@NoArgsConstructor
@AllArgsConstructor
public class StudentVO implements Serializable {

    private static final long serialVersionUID = 1L;

    @ExcelProperty("学校")
    private String schoolName;

    @ExcelProperty("班级")
    private String className;

    @ExcelProperty("姓名")
    private String studentName;

}

具体生成过程(注释已标明,为便于理解写 Controller 里了,自用请合理拆分)

@GetMapping("/exportStudentData")
    public void exportStudentData(HttpServletResponse response) throws IOException {
        /**
         * 1、设置响应头,表示这是一个 Excel 工作表文件
         * 2、定义返回的文件名
         */
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setHeader("Content-Disposition", "attachment; filename=students.xlsx");

        // 填充假数据,生产环境中替换为 DB 数据即可
        List<StudentVO> students = new ArrayList<>();
        students.add(new StudentVO("清华大学", "一班", "张三"));
        students.add(new StudentVO("清华大学", "一班", "李四"));
        students.add(new StudentVO("北京大学", "一班", "赵六"));
        students.add(new StudentVO("清华大学", "二班", "王五"));
        students.add(new StudentVO("北京大学", "二班", "小明"));

        /**
         * 创建一个临时文件,写入数据到 Excel
         * Sheet 页名称记得替换为实际的业务名称
         */
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        EasyExcel.write(outputStream, StudentVO.class).sheet("学生数据").doWrite(students);

        /**
         * 使用 Apache POI 进行筛选校验操作
         * try-with-resources 语法
         */
        try (
                InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
                XSSFWorkbook workbook = new XSSFWorkbook(inputStream);
        ) {
            // 通过 workbook.getSheetAt(0) 获取的就是工作簿中的第一个工作表
            XSSFSheet sheet = workbook.getSheetAt(0);

            // 在指定的单元格范围内启用了自动筛选功能,使用户可以通过表头对数据进行筛选,表示:起始列为第 0 列,结束列为第 2 列
            sheet.setAutoFilter(new CellRangeAddress(0, 0, 0, 2));

            // 动态获取所有学校和班级,Java8 以下替换为传统的 for 循环即可
            Set<String> schools = students.stream().map(StudentVO::getSchoolName).collect(Collectors.toSet());
            Map<String, Set<String>> schoolClassMap = new HashMap<>();
            for (StudentVO student : students) {
                /**
                 * 如果不存在就创建一个新的 HashSet
                 * 学生的班级名称添加到对应学校的 Set 中
                 */
                schoolClassMap.computeIfAbsent(student.getSchoolName(), k -> new HashSet<>()).add(student.getSchoolName());
            }

            // 创建数据验证规则
            XSSFDataValidationHelper dvHelper = new XSSFDataValidationHelper(sheet);

            // 创建单元格区域列表,表示:起始行为第 1 行,结束行为学生列表的最后一行,起始列为第 0 列,结束列为第 0 列
            CellRangeAddressList schoolAddressList = new CellRangeAddressList(1, students.size(), 0, 0);

            // 将学校名称转换为数组
            String[] schoolArray = schools.toArray(new String[0]);

            // 创建数据验证规则并应用到指定的单元格区域
            dvHelper.createValidation(dvHelper.createExplicitListConstraint(schoolArray), schoolAddressList).setShowErrorBox(true);

            // 遍历班级的 Map 集合
            for (Map.Entry<String, Set<String>> entry : schoolClassMap.entrySet()) {
                // 获取学校名称
                String school = entry.getKey();

                // 得到该学校的所有班级
                Set<String> classes = entry.getValue();
                String[] classArray = classes.toArray(new String[0]);

                // 遍历学生列表,为每个学生的班级设置数据验证规则
                for (int i = 1; i <= students.size(); i++) {
                    // 如果学校名称相同,为该学生的班级设置数据验证规则
                    if (sheet.getRow(i).getCell(0).getStringCellValue().equals(school)) {
                        CellRangeAddressList classAddressList = new CellRangeAddressList(i, i, 1, 1);
                        // 创建数据验证规则并应用到指定的单元格区域(班级)
                        dvHelper.createValidation(dvHelper.createExplicitListConstraint(classArray), classAddressList).setShowErrorBox(true);
                    }
                }
            }

            // 将 Excel 文件写入到输出流
            ByteArrayOutputStream finalOutputStream = new ByteArrayOutputStream();
            workbook.write(finalOutputStream);
            workbook.close();

            response.getOutputStream().write(finalOutputStream.toByteArray());
        }
    }

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值