使用EasyExcel实现Excel单元格保护:自由锁定表头和数据行
使用EasyExcel实现Excel单元格保护:锁定表头和第二行数据
前言
在日常开发中,我们经常需要导出Excel文件,有时还需要对Excel中的某些单元格进行保护,防止用户误修改。本文将介绍如何使用EasyExcel 4.0.3实现锁定Excel表头和第二行数据,同时允许其他单元格自由编辑的功能。
技术背景
EasyExcel是阿里巴巴开源的一个基于Java的简单、省内存的读写Excel工具。它能够帮助我们轻松实现Excel的各种操作,包括单元格样式设置、数据保护等。
实现目标
- 锁定Excel表头(第一行)
- 锁定第二行数据
- 允许第三行及之后的数据自由编辑
- 允许用户添加新行/列并编辑
Maven依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>4.0.3</version>
</dependency>
核心代码实现
1. 数据模型定义
public static class TestDataModel {
@ExcelProperty("姓名")
private String name;@ExcelProperty("年龄")
private Integer age;@ExcelProperty("城市")
private String city;// 构造方法和getter/setter省略
}
2. 自定义单元格样式策略
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. 工作表保护处理器
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主方法
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);
}
完整代码
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));// 导出ExcelexportExcel("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. 写入ExcelEasyExcel.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*/@Overridepublic 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 {@Overridepublic 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 Setterspublic 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;}}
}
关键点解析
- 样式继承机制:
- 表头行直接应用锁定样式
- 第二行显式创建并应用锁定样式
- 其他行应用解锁样式
- 工作表保护:
- 设置所有列默认可编辑
- 启用工作表保护(密码保护)
- 只有锁定样式的单元格受到保护
- 单元格处理流程:
- 每个单元格独立处理
- 样式应用是逐个单元格进行的
- 显式设置覆盖所有默认行为
效果验证
生成的Excel文件将具有以下特性:
- 锁定区域:
- 第一行(表头):完全锁定
- 第二行(第一行数据):完全锁定
- 可编辑区域:
- 第三行及之后的数据行
- 工作表的空白区域
- 新添加的行/列
- 保护机制:
- 需要密码"123"才能修改受保护区域
- 可编辑区域可以直接修改
总结
通过EasyExcel的样式策略和工作表保护机制,我们实现了灵活控制Excel单元格编辑权限的功能。这种方案具有以下优点:
- 精确控制:可以精确锁定特定行/列
- 灵活性:不影响用户在其他区域的编辑
- 兼容性:适用于各种数据模型
- 易扩展:可以轻松调整锁定策略
这种实现方式特别适用于需要保护表头和示例数据,同时允许用户自由编辑其他数据的场景,如数据模板导出等。