使用EasyExcel实现Excel单元格保护:自由锁定表头和数据行

使用EasyExcel实现Excel单元格保护:锁定表头和第二行数据

前言

在日常开发中,我们经常需要导出Excel文件,有时还需要对Excel中的某些单元格进行保护,防止用户误修改。本文将介绍如何使用EasyExcel 4.0.3实现锁定Excel表头和第二行数据,同时允许其他单元格自由编辑的功能。

技术背景

EasyExcel是阿里巴巴开源的一个基于Java的简单、省内存的读写Excel工具。它能够帮助我们轻松实现Excel的各种操作,包括单元格样式设置、数据保护等。

实现目标

  • 锁定Excel表头(第一行)
  • 锁定第二行数据
  • 允许第三行及之后的数据自由编辑
  • 允许用户添加新行/列并编辑

Maven依赖

xml 复制代码
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>4.0.3</version>
</dependency>

核心代码实现

1. 数据模型定义

java 复制代码
public static class TestDataModel {
@ExcelProperty("姓名")
private String name;

@ExcelProperty("年龄")
private Integer age;

@ExcelProperty("城市")
private String city;

// 构造方法和getter/setter省略
}

2. 自定义单元格样式策略

java 复制代码
static class CustomCellStyleStrategy extends HorizontalCellStyleStrategy {
public CustomCellStyleStrategy(WriteCellStyle headWriteCellStyle, WriteCellStyle contentWriteCellStyle) {
super(headWriteCellStyle, contentWriteCellStyle);
}

@Override
public void afterCellDispose(CellWriteHandlerContext context) {
// 表头行处理
if (context.getHead()) {
super.afterCellDispose(context);
return;
}

// 数据行处理
if (context.getRowIndex() != null) {
if (context.getRowIndex() == 1) {
// 第二行数据,应用锁定样式
Cell cell = context.getCell();
Workbook workbook = context.getWriteSheetHolder().getSheet().getWorkbook();

// 创建新样式(基于当前样式)
CellStyle newStyle = workbook.createCellStyle();
if (cell.getCellStyle() != null) {
newStyle.cloneStyleFrom(cell.getCellStyle());
}
newStyle.setLocked(true); // 设置为锁定
cell.setCellStyle(newStyle);
} else {
// 其他行应用解锁样式
super.afterCellDispose(context);
}
}
}
}

3. 工作表保护处理器

java 复制代码
static class SheetProtectionHandler implements SheetWriteHandler {
@Override
public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
Sheet sheet = writeSheetHolder.getSheet();
Workbook workbook = writeWorkbookHolder.getWorkbook();

// 设置整个工作表的默认样式为可编辑
CellStyle defaultStyle = workbook.createCellStyle();
defaultStyle.setLocked(false);

// 设置所有列的默认样式
int maxColumns = 256;
for (int i = 0; i < maxColumns; i++) {
sheet.setDefaultColumnStyle(i, defaultStyle);
}

// 启用工作表保护(密码:123)
sheet.protectSheet("123");
}
}

4. 导出Excel主方法

java 复制代码
public static void exportExcel(String fileName, List<?> dataList) {
// 定义锁定样式
WriteCellStyle lockedStyle = new WriteCellStyle();
lockedStyle.setLocked(true);

// 定义解锁样式
WriteCellStyle unlockedStyle = new WriteCellStyle();
unlockedStyle.setLocked(false);

// 创建处理器实例
HorizontalCellStyleStrategy styleStrategy = new CustomCellStyleStrategy(lockedStyle, unlockedStyle);
SheetWriteHandler sheetProtectionHandler = new SheetProtectionHandler();

// 写入Excel
EasyExcel.write(fileName, TestDataModel.class)
.registerWriteHandler(styleStrategy)
.registerWriteHandler(sheetProtectionHandler)
.sheet("员工数据")
.doWrite(dataList);
}

完整代码

java 复制代码
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;

import java.util.ArrayList;
import java.util.List;

public class ExcelExporter {

    public static void main(String[] args) {
        // 创建测试数据
        List<TestDataModel> dataList = new ArrayList<>();
        dataList.add(new TestDataModel("张三", 25, "北京"));
        dataList.add(new TestDataModel("李四", 30, "上海"));
        dataList.add(new TestDataModel(null, 28, "广州"));
        dataList.add(new TestDataModel("赵六", 35, null));

        // 导出Excel
        exportExcel("test_output.xlsx", dataList);
        System.out.println("Excel文件已生成: test_output.xlsx");
    }

    public static void exportExcel(String fileName, List<?> dataList) {
        // 1. 定义锁定样式(用于表头和第二行)
        WriteCellStyle lockedStyle = new WriteCellStyle();
        lockedStyle.setLocked(true); // 设置单元格锁定

        // 2. 定义解锁样式(用于第三行及之后的数据)
        WriteCellStyle unlockedStyle = new WriteCellStyle();
        unlockedStyle.setLocked(false); // 设置单元格可编辑

        // 3. 创建自定义样式策略
        HorizontalCellStyleStrategy styleStrategy = new CustomCellStyleStrategy(lockedStyle, unlockedStyle);

        // 4. 创建工作表保护处理器
        SheetWriteHandler sheetProtectionHandler = new SheetProtectionHandler();

        // 5. 写入Excel
        EasyExcel.write(fileName, TestDataModel.class).registerWriteHandler(styleStrategy).registerWriteHandler(sheetProtectionHandler).sheet("员工数据").doWrite(dataList);
    }

    /**
     * 自定义单元格样式策略
     */
    static class CustomCellStyleStrategy extends HorizontalCellStyleStrategy {
        public CustomCellStyleStrategy(WriteCellStyle headWriteCellStyle, WriteCellStyle contentWriteCellStyle) {
            super(headWriteCellStyle, contentWriteCellStyle);
        }

        /**
         * 重写方法,根据行号应用不同样式
         *
         * @param context
         */
        @Override
        public void afterCellDispose(CellWriteHandlerContext context) {
            // 表头行(isHead=true)直接使用父类方法,即应用锁定样式
            if (context.getHead()) {
                super.afterCellDispose(context);
                return;
            }
            // 数据行处理,数据行的格式是解锁还是锁定,要看创建时传参
            if (context.getRowIndex() != null) {
                if (context.getRowIndex() == 1) {
                    // 获取当前单元格
                    Cell cell = context.getCell();
                    Workbook workbook = context.getWriteSheetHolder().getSheet().getWorkbook();
                    // 1. 创建新样式(基于当前样式)
                    CellStyle newStyle = workbook.createCellStyle();
                    // 2. 复制当前样式(如果存在)
                    if (cell.getCellStyle() != null) {
                        newStyle.cloneStyleFrom(cell.getCellStyle());
                    }
                    // 3. 设置为锁定
                    newStyle.setLocked(true);
                    // 4. 应用新样式
                    cell.setCellStyle(newStyle);
                } else {
                    super.afterCellDispose(context);
                }
            }
        }

    }

    /**
     * 工作表保护处理器
     */
    static class SheetProtectionHandler implements SheetWriteHandler {
        @Override
        public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
            Sheet sheet = writeSheetHolder.getSheet();
            Workbook workbook = writeWorkbookHolder.getWorkbook();

            // 1. 设置整个工作表的默认样式为可编辑
            CellStyle defaultStyle = workbook.createCellStyle();
            defaultStyle.setLocked(false); // 解锁所有单元格

            // 设置所有列的默认样式(假设最大列数为256)
            int maxColumns = 256;
            for (int i = 0; i < maxColumns; i++) {
                sheet.setDefaultColumnStyle(i, defaultStyle);
            }

            // 3. 启用工作表保护(使用空密码)
            sheet.protectSheet("123");
        }

    }

    // 测试数据模型
    public static class TestDataModel {

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

        @ExcelProperty("年龄")
        private Integer age;

        @ExcelProperty("城市")
        private String city;

        public TestDataModel() {
            // 无参构造函数
        }

        public TestDataModel(String name, Integer age, String city) {
            this.name = name;
            this.age = age;
            this.city = city;
        }

        // Getters and Setters
        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Integer getAge() {
            return age;
        }

        public void setAge(Integer age) {
            this.age = age;
        }

        public String getCity() {
            return city;
        }

        public void setCity(String city) {
            this.city = city;
        }
    }
}

关键点解析

  1. 样式继承机制
  • 表头行直接应用锁定样式
  • 第二行显式创建并应用锁定样式
  • 其他行应用解锁样式
  1. 工作表保护
  • 设置所有列默认可编辑
  • 启用工作表保护(密码保护)
  • 只有锁定样式的单元格受到保护
  1. 单元格处理流程
  • 每个单元格独立处理
  • 样式应用是逐个单元格进行的
  • 显式设置覆盖所有默认行为

效果验证

生成的Excel文件将具有以下特性:

  1. 锁定区域
  • 第一行(表头):完全锁定
  • 第二行(第一行数据):完全锁定
  1. 可编辑区域
  • 第三行及之后的数据行
  • 工作表的空白区域
  • 新添加的行/列
  1. 保护机制
  • 需要密码"123"才能修改受保护区域
  • 可编辑区域可以直接修改

总结

通过EasyExcel的样式策略和工作表保护机制,我们实现了灵活控制Excel单元格编辑权限的功能。这种方案具有以下优点:

  1. 精确控制:可以精确锁定特定行/列
  2. 灵活性:不影响用户在其他区域的编辑
  3. 兼容性:适用于各种数据模型
  4. 易扩展:可以轻松调整锁定策略

这种实现方式特别适用于需要保护表头和示例数据,同时允许用户自由编辑其他数据的场景,如数据模板导出等。

相关推荐
Moss Huang27 分钟前
docker-runc not installed on system
java·docker·容器
麦兜*2 小时前
Spring Boot 集成 Docker 构建与发版完整指南
java·spring boot·后端·spring·docker·系统架构·springcloud
Cisyam^2 小时前
Go环境搭建实战:告别Java环境配置的复杂
java·开发语言·golang
青云交4 小时前
Java 大视界 -- 基于 Java 的大数据实时流处理在智能电网分布式电源接入与电力系统稳定性维护中的应用(404)
java·大数据·分布式·智能电网·flink 实时流处理·kafka 数据采集·iec 61850 协议
葡萄城技术团队4 小时前
借助 LAMBDA 公式,实现单元格区域高效转换
excel
仰望星空@脚踏实地5 小时前
maven scope 详解
java·maven·scope
M_Reus_115 小时前
Groovy集合常用简洁语法
java·开发语言·windows
带刺的坐椅5 小时前
10分钟带你体验 Solon 的状态机
java·solon·状态机·statemachine
小鹅叻5 小时前
MyBatis题
java·tomcat·mybatis