使用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單元格編輯權限的功能。這種方案具有以下優點:
- 精確控制:可以精確鎖定特定行/列
- 靈活性:不影響用戶在其他區域的編輯
- 兼容性:適用于各種數據模型
- 易擴展:可以輕松調整鎖定策略
這種實現方式特別適用于需要保護表頭和示例數據,同時允許用戶自由編輯其他數據的場景,如數據模板導出等。