使用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));
// 导出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;
}
}
}
关键点解析
- 样式继承机制:
- 表头行直接应用锁定样式
- 第二行显式创建并应用锁定样式
- 其他行应用解锁样式
- 工作表保护:
- 设置所有列默认可编辑
- 启用工作表保护(密码保护)
- 只有锁定样式的单元格受到保护
- 单元格处理流程:
- 每个单元格独立处理
- 样式应用是逐个单元格进行的
- 显式设置覆盖所有默认行为
效果验证
生成的Excel文件将具有以下特性:
- 锁定区域:
- 第一行(表头):完全锁定
- 第二行(第一行数据):完全锁定
- 可编辑区域:
- 第三行及之后的数据行
- 工作表的空白区域
- 新添加的行/列
- 保护机制:
- 需要密码"123"才能修改受保护区域
- 可编辑区域可以直接修改
总结
通过EasyExcel的样式策略和工作表保护机制,我们实现了灵活控制Excel单元格编辑权限的功能。这种方案具有以下优点:
- 精确控制:可以精确锁定特定行/列
- 灵活性:不影响用户在其他区域的编辑
- 兼容性:适用于各种数据模型
- 易扩展:可以轻松调整锁定策略
这种实现方式特别适用于需要保护表头和示例数据,同时允许用户自由编辑其他数据的场景,如数据模板导出等。