如何在使用 java 库创建 excel (xls) 文件时锁定单元格

how to lock cell while creating an excel (xls) file using a java library

我们如何使用任何 java 库创建 excel 文件 (xls),使单元格不 select能够,即:用户不应该能够 select 或复制任何数据。

我知道如果我们需要 xlsx 文件,我们可以通过使用 apache-poi XSSF 库实现这一点,即:XSSFSheet.lockSelectLockedCells(boolean enabled) 但不确定如何使用 HSSFSheet 因为我只需要创建 xls 个文件

之前有人提过这个问题,我想这就是你想要的:

Apache poi 暂时不支持 HSSFSheet 保护锁设置。它仅支持使用默认锁定设置的 sheet 保护。

2017年我就已经给出了解决方案。参见

使用当前的 apache poi 版本,它现在需要覆盖多个抽象方法。我用一些未实现的方法更新了我的答案,只是为了让它更有效。

新见解表明 SheetProtectionRecord 实际上是 Shared Features Header Record having the SharedFeatureType ISFPROTECTION. The rgbHdrData is EnhancedProtection 数据。

所以我扩展了我的 SheetProtectionRecord 并将提供一种方法 SheetProtectionRecord getOraddSheetProtectionRecord(HSSFSheet hssfSheet),它能够从 HSSFSheet 中获取 SheetProtectionRecord 或在必要时添加一个新的。然后我们可以使用它来设置 fr sheet 保护的所有锁定设置。

完整示例:

import java.io.*;

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.hssf.usermodel.*;

import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.FeatHdrRecord;
import org.apache.poi.hssf.record.RecordBase;
import org.apache.poi.hssf.model.InternalSheet;

import java.lang.reflect.Field;

import java.util.List;

public class ExcelLockCells {
  
 static SheetProtectionRecord getOraddSheetProtectionRecord(HSSFSheet hssfSheet) throws Exception {  
  InternalSheet internalsheet = hssfSheet.getSheet(); 

  Field _records = InternalSheet.class.getDeclaredField("_records");
  _records.setAccessible(true);
  @SuppressWarnings("unchecked") 
  List<RecordBase> records = (List<RecordBase>)_records.get(internalsheet);
  
  byte[] data = null;
  
  //try to find sheet protection record which is a shared features record having type SHAREDFEATURES_ISFPROTECTION - 2
  //if found, get data and remove
  boolean sheetProtectionRecordFound = false;
  for (int i = records.size()-1; i >=0; i--) {
   RecordBase recordBase = records.get(i);
   if (recordBase instanceof FeatHdrRecord) {
    FeatHdrRecord record = (FeatHdrRecord)recordBase; 
    int recordSize = record.getRecordSize(); //includes additional the sid and reclength (4 bytes)
    byte[] recContent = record.serialize();
    data = new byte[recordSize-4];
    for (int j = 4; j < recContent.length; j++) data[j-4] = recContent[j];
    if (data[12] == 0x02 && data[13] == 0x00) { //SHAREDFEATURES_ISFPROTECTION - 2
     sheetProtectionRecordFound = true;
     //System.out.println(record);
     records.remove(record);
    }
   }       
  }

  SheetProtectionRecord sheetProtectionRecord = null; 
  if (sheetProtectionRecordFound && data != null) {
   sheetProtectionRecord = new SheetProtectionRecord(data);
  } else {
   sheetProtectionRecord = new SheetProtectionRecord();     
  }
  records.add(records.size() - 1, sheetProtectionRecord); 
  
  //System.out.println(sheetProtectionRecord);
  
  return sheetProtectionRecord;
 }

 public static void main(String[] args) throws Exception {
  //Workbook workbook = WorkbookFactory.create(new FileInputStream("./ExcelTemplate.xlsx")); String filePath = "./ExcelLockCells.xlsx";
  Workbook workbook = WorkbookFactory.create(new FileInputStream("./ExcelTemplate.xls")); String filePath = "./ExcelLockCells.xls";

  Sheet sheet = workbook.getSheetAt(0);

  //set lock select locked cells
  if (sheet instanceof XSSFSheet) {
   XSSFSheet xssfSheet= (XSSFSheet)sheet;   
   xssfSheet.lockSelectLockedCells(true);   
  } else if (sheet instanceof HSSFSheet) {
   HSSFSheet hssfSheet= (HSSFSheet)sheet; 
   SheetProtectionRecord sheetProtectionRecord = getOraddSheetProtectionRecord(hssfSheet);
   sheetProtectionRecord.lockSelectLockedCells(true);
  }
  
  //protect sheet
  sheet.protectSheet("");

  FileOutputStream fileOut = new FileOutputStream(filePath);
  workbook.write(fileOut);
  fileOut.close();
  workbook.close();
 }
}

二手 SheetProtectionRecord.java:

import org.apache.poi.hssf.record.StandardRecord;
import org.apache.poi.hssf.record.HSSFRecordTypes;
import org.apache.poi.util.LittleEndianOutput;
import org.apache.poi.util.GenericRecordUtil;

import java.util.Map;
import java.util.function.Supplier;

public class SheetProtectionRecord extends StandardRecord {

 //see https://docs.microsoft.com/en-us/openspecs/office_file_formats/ms-xls/5748f633-4a5c-4b2c-9f45-2d21c06f753d
 //https://docs.microsoft.com/en-us/openspecs/office_file_formats/ms-xls/4dc13a80-f10a-46e6-b55d-1df4c90508e8
 //https://docs.microsoft.com/en-us/openspecs/office_file_formats/ms-xls/f6b5a32d-7562-4124-882f-badecdc512eb
 
 private byte[] data; 

 public SheetProtectionRecord() {
  super();
  this.data = new byte[]{
   (byte)0x67, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //frtHeader (12 bytes): An FrtHeader structure. The frtHeader.rt MUST be 0x0867.
   0x02, 0x00, //isf (2 bytes): A SharedFeatureType enumeration that specifies the type of Shared Feature. ISFPROTECTION
   0x01, //reserved (1 byte):  Reserved and MUST be 1.
   (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, //cbHdrData (4 bytes): An unsigned integer that specifies whether rgbHdrData exists. rgbHdrData MUST exist.
   0x00, (byte)0x44, 0x00, 0x00 //rgbHdrData (variable) EnhancedProtection
  }; 
 }
 
 public SheetProtectionRecord(byte[] data) {
  super();
  this.data = data;
 }
 
 private SheetProtectionRecord(SheetProtectionRecord other) {
  super(other);
  this.data = other.data;
 }

 public int getDataSize() { 
  return 23; 
 }

 public short getSid() {
  return (short)0x0867;
 }

 public void lockObjects(boolean lock) {
  if(lock) data[19] &= 0xFE;
  else data[19] |= 0x01;
 }
 
 public void lockScenarios(boolean lock) {
  if(lock) data[19] &= 0xFD;
  else data[19] |= 0x02;
 }
 
 public void lockFormatCells(boolean lock) {
  if(lock) data[19] &= 0xFB;
  else data[19] |= 0x04;
 }
 
 public void lockFormatColumns(boolean lock) {
  if(lock) data[19] &= 0xF7;
  else data[19] |= 0x08;
 }
 
 public void lockFormatRows(boolean lock) {
  if(lock) data[19] &= 0xEF;
  else data[19] |= 0x10;
 }
 
 public void lockInsertColumns(boolean lock) {
  if(lock) data[19] &= 0xDF;
  else data[19] |= 0x20;
 }
 
 public void lockInsertRows(boolean lock) {
  if(lock) data[19] &= 0xBF;
  else data[19] |= 0x40;
 }

 public void lockInsertHyperlinks(boolean lock) {
  if(lock) data[19] &= 0x7F;
  else data[19] |= 0x80;
 }
 
 public void lockDeleteColumns(boolean lock) {
  if(lock) data[20] &= 0xFE;
  else data[20] |= 0x01;
 }
 
 public void lockDeleteRows(boolean lock) {
  if(lock) data[20] &= 0xFD;
  else data[20] |= 0x02;
 }

 public void lockSelectLockedCells(boolean lock) {
  if(lock) data[20] &= 0xFB;
  else data[20] |= 0x04;
 }
 
 public void lockSort(boolean lock) {
  if(lock) data[20] &= 0xF7;
  else data[20] |= 0x08;
 }
 
 public void lockAutoFilter(boolean lock) {
  if(lock) data[20] &= 0xEF;
  else data[20] |= 0x10;
 } 

 public void lockPivotTables(boolean lock) {
  if(lock) data[20] &= 0xDF;
  else data[20] |= 0x20;
 } 

 public void lockSelectUnLockedCells(boolean lock) {
  if(lock) data[20] &= 0xBF;
  else data[20] |= 0x40;
 }

 public void serialize(LittleEndianOutput out) {
  out.write(data);
 }
 
 @Override
 public SheetProtectionRecord copy() {
  return new SheetProtectionRecord(this);
 }

 @Override
 public HSSFRecordTypes getGenericRecordType() {
  return HSSFRecordTypes.FEAT_HDR;
 }
  
 @Override
 public Map<String, Supplier<?>> getGenericProperties() {
  //return null; // not supported
  return GenericRecordUtil.getGenericProperties(
   "sid", () -> getSid(),
   "type", () -> "FeatHdrRecord.SHAREDFEATURES_ISFPROTECTION"
  );
 }
}

这一切都经过测试,可以使用电流 apache poi 5.0.0。以前的版本会失败。