EasyExcel锁定指定单元格 & 禁止表格复制

3.0版本:https://github.com/asdfLiang/easy-excel-test
2.0版本:https://github.com/asdfLiang/easyexcel-low-test

一、目标效果

  1. 导出一个excel表格,如果单元格中有内容,则进行锁定不允许修改;如果没有,则不锁定允许修改;
  2. 禁止用户复制sheet,防止复制整个sheet到其他excel中进行修改。
    最终效果如下:
    在这里插入图片描述
    表头和灰色的文字是导出时就有的,不允许修改;4C、5C这两个单元格导出时是空的,允许修改。其他单元格都不允许修改(包括行>5、列>G的单元格)。整个sheet无法选中,也无法进行复制。

二、依赖

    <dependencies>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>3.1.1</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

三、代码实现

测试代码入口

import com.alibaba.excel.EasyExcel;
import excel.CustomSheetWriteHandler;
import excel.StyleWriteHandler;
import org.junit.Test;

import java.io.File;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author by liangzj
 * @since 2022/9/17 15:32
 */
public class EasyExcelTest {

    @Test
    public void testWriteExcel() {
        String pathname = "E:\\liangzj\\Desktop\\test.xlsx";
        EasyExcel.write(new File(pathname))
                .head(header())
                .registerWriteHandler(new StyleWriteHandler())
                .registerWriteHandler(new CustomSheetWriteHandler())
                .sheet("Sheet1")
                .doWrite(data());
    }

    /**
     * 数据是先行后列
     *
     * @return
     */
    public List<List<String>> data() {
        List<List<String>> data = getRowColMatrix(3, 5);

        data.get(0).set(0, "用户1");
        data.get(0).set(1, "1234567890");
        data.get(0).set(2, "合同1");
        data.get(0).set(3, "文本1");
        data.get(0).set(4, "210283202209078615");

        data.get(1).set(0, "用户2");
        data.get(1).set(1, "1234553478");
        data.get(1).set(2, null);
        data.get(1).set(3, "文本2");
        data.get(1).set(4, "210211202209073951");

        data.get(2).set(0, "用户3");
        data.get(2).set(1, "8332675567");
        data.get(2).set(2, null);
        data.get(2).set(3, "文本3");
        data.get(2).set(4, "120221202209076790");

        return data;
    }

    /**
     * 表头是先列后行
     *
     * @return
     */
    public List<List<String>> header() {
        List<List<String>> header = getColRowMatrix(2, 5);
        header.get(0).set(0, "姓名");
        header.get(0).set(1, "姓名");
        header.get(1).set(0, "手机/邮箱");
        header.get(1).set(1, "手机/邮箱");
        header.get(2).set(0, "合同名称");
        header.get(2).set(1, "合同名称");
        header.get(3).set(0, "文件1");
        header.get(3).set(1, "单行文本");
        header.get(4).set(0, "文件1");
        header.get(4).set(1, "身份证号");

        return header;
    }

    /**
     * 生成一个先行后列的矩阵数组
     *
     * @param maxRow
     * @param maxCol
     * @return
     */
    private static List<List<String>> getRowColMatrix(int maxRow, int maxCol) {
        List<List<String>> header =
                Stream.generate(
                                () ->
                                        Stream.generate(() -> "")
                                                .limit(maxCol)
                                                .collect(Collectors.toList()))
                        .limit(maxRow)
                        .collect(Collectors.toList());
        return header;
    }

    /**
     * 生成一个先列后行的矩阵数组
     *
     * @param maxRow
     * @param maxCol
     * @return
     */
    private static List<List<String>> getColRowMatrix(int maxRow, int maxCol) {
        return getRowColMatrix(maxCol, maxRow);
    }
}

设置保护表格

((SXSSFSheet) writeSheetHolder.getSheet()).lockSelectLockedCells(true);这行代码设置的是"已锁定的单元格不可复制",效果就是别人无法复制整个sheet,这样可以防止别人把内容复制到其他excel表中进行修改。如果允许复制,可以不加,不会影响锁单元格的效果。

package excel;

import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import org.apache.poi.xssf.streaming.SXSSFSheet;

/**
 * @author by liangzj
 * @since 2022/9/17 16:08
 */
public class CustomSheetWriteHandler implements SheetWriteHandler {

    @Override
    public void afterSheetCreate(
            WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
        // 设置保护密码
        writeSheetHolder.getSheet().protectSheet("123456");
        // 锁定单元格不可选中(防止别人直接复制内容到其他excel修改)
        ((SXSSFSheet) writeSheetHolder.getSheet()).lockSelectLockedCells(true);
    }
}

设置单元格锁定状态

contentStyle和contentStyle2实现的效果是一样的,都是设置指定的单元格是否锁定。contentStyle2方法要注意不能直接cell.getCellStyle().setLocked(true),这么写无法生效。
注意:解锁单元格的关键代码是:cellStyle.setLocked(false),方法1中是writeCellStyle.setLocked(false)。

package excel;

import com.alibaba.excel.util.StringUtils;
import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.IndexedColors;

/**
 * 单元格样式处理器
 *
 * @author by liangzj
 * @since 2022/9/17 16:26
 */
public class StyleWriteHandler extends LongestMatchColumnWidthStyleStrategy {

    @Override
    public void afterCellDispose(CellWriteHandlerContext context) {
        if (context.getHead()) { // 表头属性设置
            headerStyle(context);
        } else { // 表数据属性设置
            //            contentStyle(context);
            contentStyle2(context.getCell());
        }
    }

    /**
     * 解锁没有内容的单元格(方法1)
     *
     * @param context
     */
    private void contentStyle(CellWriteHandlerContext context) {
        WriteCellStyle writeCellStyle = context.getFirstCellData().getOrCreateStyle();
        /* !! 注意:这行就是解锁单元格的代码,locked == true为锁定,locked == false为不锁定 */
        writeCellStyle.setLocked(StringUtils.isNotBlank(context.getCell().getStringCellValue()));
        
        // 如果锁定,置灰(这行只是设置样式,不重要)
        if (writeCellStyle.getLocked()) {
            WriteFont writeFont = new WriteFont();
            writeFont.setColor(IndexedColors.GREY_40_PERCENT.index);
            writeCellStyle.setWriteFont(writeFont);
        }
    }

    /**
     * 解锁没有内容的单元格(方法2)
     *
     * @param cell
     */
    private void contentStyle2(Cell cell) {
    	// 创建一个新的单元格样式
        CellStyle cellStyle = cell.getSheet().getWorkbook().createCellStyle();
        // 复制原来单元格的样式(这样就能保证原来单元格的样式不丢失)
        cellStyle.cloneStyleFrom(cell.getCellStyle());
        /* !! 注意:这行就是解锁单元格的代码,locked == true为锁定,locked == false为不锁定 */
        cellStyle.setLocked(StringUtils.isNotBlank(cell.getStringCellValue()));
        // 把新建的样式设置为当前单元格样式
        cell.setCellStyle(cellStyle);
        
        // 如果锁定,置灰(样式设置,不重要)
        if (cell.getCellStyle().getLocked()) {
            Font font = cell.getSheet().getWorkbook().createFont();
            font.setColor(IndexedColors.GREY_40_PERCENT.index);
            cellStyle.setFont(font);
        }
    }

    /**
     * 表头格式处理
     *
     * @param context
     */
    private static void headerStyle(CellWriteHandlerContext context) {
        Cell cell = context.getCell();

        int colWidth = cell.getStringCellValue().length() * 1500;
        boolean needHidden = "requireId".equals(cell.getStringCellValue());

        // 根据表头文字设置列宽
        cell.getSheet().setColumnWidth(cell.getColumnIndex(), colWidth);
        // 冻结表头
        cell.getSheet().createFreezePane(1, 2);
        // 隐藏指定列
        cell.getSheet().setColumnHidden(cell.getColumnIndex(), needHidden);
    }
}

2.0版本

如果你用的是2.0版本的EasyExcel,那么写法稍有不同,如下(注意:解锁单元格的关键代码是:cellStyle.setLocked(false)):

package excel;

import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.util.StringUtils;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.IndexedColors;

import java.util.List;

/**
 * @author by liangzj
 * @since 2022/9/17 16:26
 */
public class StyleWriteHandler extends LongestMatchColumnWidthStyleStrategy {

    @Override
    public void afterCellDispose(
            WriteSheetHolder writeSheetHolder,
            WriteTableHolder writeTableHolder,
            List<CellData> cellDataList,
            Cell cell,
            Head head,
            Integer relativeRowIndex,
            Boolean isHead) {
        super.afterCellDispose(
                writeSheetHolder,
                writeTableHolder,
                cellDataList,
                cell,
                head,
                relativeRowIndex,
                isHead);

        if (isHead) { // 表头属性设置
            headerStyle(cell);
        } else { // 表数据属性设置
            contentStyle(cell);
        }
    }

    private void contentStyle(Cell cell) {
    	// 创建一个新的单元格样式
        CellStyle cellStyle = cell.getSheet().getWorkbook().createCellStyle();
        // 复制原单元格的样式(这样就能保证单元格原有的样式不丢失)
        cellStyle.cloneStyleFrom(cell.getCellStyle());
        /* !! 注意:这行就是解锁单元格的代码,locked == true为锁定,locked == false为不锁定 */
        cellStyle.setLocked(!StringUtils.isEmpty(cell.getStringCellValue()));
        // 把新创建的样式设置为当前单元格的样式
        cell.setCellStyle(cellStyle);

		// 不可编辑的单元格置灰(样式设置,不重要)
        if (cell.getCellStyle().getLocked()) {
            Font font = cell.getSheet().getWorkbook().createFont();
            font.setColor(IndexedColors.GREY_40_PERCENT.getIndex());
            cell.getCellStyle().setFont(font);
        } else {
            cell.setCellValue("可填写");
        }
    }

    private static void headerStyle(Cell cell) {
        int colWidth = cell.getStringCellValue().length() * 1500;

        // 根据表头文字设置列宽
        cell.getSheet().setColumnWidth(cell.getColumnIndex(), colWidth);
        // 冻结表头
        cell.getSheet().createFreezePane(1, 2);
    }
}

四、总结

点击执行最上方的测试代码,即可在指定位置生成一个excel文件。总结一下,这个实现思路是,先锁定sheet的所有单元格,再在对允许修改单元格进行解锁。实现这个效果的关键点:

  1. 设置保护单元格,没有这个设置,锁定单元格不会生效;
  2. 设置单元格保护状态,注意一定要生成一个新的cellStyle,不要直接cell.getCellStyle().setLocked(true);
  3. 创建新的cellStyle后,加上cellStyle.cloneStyleFrom(cell.getCellStyle()),这样可以保证原有的单元格样式不丢失。
使用EasyExcel写入指定单元格可以通过注解的方式来实现,具体步骤如下: 1. 在实体类中添加注解,用于指定数据写入的单元格,如下所示: ``` public class DemoData { // @ExcelProperty 注解用于指定数据写入的单元格,index 表示列的位置,value 表示列的名称 @ExcelProperty(index = 0, value = "姓名") private String name; @ExcelProperty(index = 1, value = "年龄") private Integer age; // ... 其他属性 } ``` 2. 创建需要写入的数据列表,并指定写入的文件路径,如下所示: ``` // 创建需要写入的数据列表 List<DemoData> list = new ArrayList<>(); list.add(new DemoData("张三", 20)); list.add(new DemoData("李四", 21)); list.add(new DemoData("王五", 22)); // 指定写入的文件路径 String fileName = "D:\\demo.xlsx"; ``` 3. 使用EasyExcel的`write`方法进行数据写入,使用`Sheet`对象的`setWriteStartPosition`方法指定数据写入的起始位置,如下所示: ``` // EasyExcel写入数据 ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build(); Sheet sheet = new Sheet(1, 0, DemoData.class); // 指定数据写入的起始位置 sheet.setWriteStartPosition(1, 0); excelWriter.write(list, sheet); excelWriter.finish(); ``` 在上述代码中,`Sheet`构造方法的第一个参数表示写入的sheet页码(从1开始),第二个参数表示写入的起始行号(从0开始),第三个参数表示实体类。 注意,需要在pom.xml中添加EasyExcel的依赖,如下所示: ``` <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>2.2.10</version> </dependency> ```
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值