EasyExcel 导入模板踩坑:row.add(““)导致文本格式失效与单元格保护问题解决方案

目录

在处理导入模板时遇到一个矛盾:为了实现指定列保护,必须提前创建"导入月份"列的 Cell(通过 row.add("")),否则该列不存在,无法设置 locked=false;但创建空字符串单元格后,EasyExcel 对空字符串和非空字符串的处理路径不同,导致文本格式 @ 失效,用户输入 2026-01 时仍可能被 Excel 自动识别为日期。而直接使用 row.add("2026-01") 时,由于单元格被识别为字符串类型,文本格式可以正常生效。

一、问题背景

生成 Excel 导入模板时,经常遇到两个问题:

  1. 指定列需要保护,用户不能修改;
  2. 日期类字符串(如 2026-01)不能被 Excel 自动转换。

例如:

text 复制代码
2026-01

Excel 默认可能变成:

text 复制代码
Jan-26

同时,在使用 EasyExcel 生成模板时,还会遇到一个特殊问题:

row.add("2026-01") 可以保持文本格式,但是 row.add("") 却可能导致文本格式失效。

原因在于 EasyExcel 对空字符串和非空字符串的处理路径不同。


二、列保护问题

Excel 开启保护后:

text 复制代码
locked=true  → 不可编辑
locked=false → 可编辑

但是 Excel 单元格默认:

text 复制代码
locked=true

所以:

只设置需要保护列:

java 复制代码
style.setLocked(true);

是不够的。

必须同时设置:

text 复制代码
保护列:
locked=true

非保护列:
locked=false

否则开启:

java 复制代码
sheet.protectSheet("123456");

后,所有列都会锁定。


三、为什么需要 maxColumnIndex

例如业务数据:

java 复制代码
row.add("序号");
row.add("单位");
row.add("项目");
row.add("类型");
row.add("面积");
row.add("等级");

实际只有:

text 复制代码
A-F

但是模板需要:

text 复制代码
A-J

如果使用:

java 复制代码
row.getLastCellNum()

只能得到已有列:

text 复制代码
6

导致 G-J 没有处理。

开启保护后:

text 复制代码
G-J 默认 locked=true

结果:

text 复制代码
全部列锁定

原因:

Excel 中不存在的 Cell 无法通过 Handler 修改样式。

解决:

增加:

java 复制代码
exportSheet.setMaxColumnIndex(9);

Handler 按:

java 复制代码
0~9

遍历所有列。


四、为什么 row.add("") 会导致文本失效

很多人认为:

java 复制代码
row.add("");

只是创建一个空单元格。

实际上,EasyExcel 对:

java 复制代码
row.add("2026-01")

和:

java 复制代码
row.add("")

处理路径不同。


1. 非空字符串处理路径

例如:

java 复制代码
row.add("2026-01");

EasyExcel 会认为:

text 复制代码
这是一个有效字符串数据

处理流程:

text 复制代码
String("2026-01")
        ↓
WriteCellData<String>
        ↓
CellType.STRING
        ↓
写入Excel
        ↓
应用CellStyle

此时单元格已经明确为字符串类型。

因此:

text 复制代码
2026-01

通常不会被 Excel 自动转换。


2. 空字符串处理路径

例如:

java 复制代码
row.add("");

EasyExcel 会将其视为空内容处理。

流程可能变为:

text 复制代码
空字符串("")
        ↓
判断无有效内容
        ↓
创建空白Cell或Blank类型Cell
        ↓
后续样式处理

此时:

  • 没有实际字符串值;
  • 单元格类型不是明确的 STRING;
  • Excel 打开后可能仍按照默认格式 General 处理。

最终:

text 复制代码
单元格格式:
General

用户输入:

text 复制代码
2026-01

Excel 根据常规规则自动识别:

text 复制代码
Jan-26

五、正确方案

不要业务代码补:

java 复制代码
row.add("");

因为:

java 复制代码
row.add("")

只是创建空数据,并不能保证文本格式。

推荐:

1. ExportSheet 增加列数

java 复制代码
private Integer maxColumnIndex;

例如:

java 复制代码
exportSheet.setMaxColumnIndex(9);

2. Handler 创建缺失 Cell(待验证,当前方案未生效)

使用:

java 复制代码
Cell cell =
    row.getCell(
        columnIndex,
        Row.MissingCellPolicy.CREATE_NULL_AS_BLANK
    );

作用:

即使业务数据没有这一列,也主动创建 Cell。

然后统一设置:

锁定

java 复制代码
style.setLocked(true/false);

文本

java 复制代码
style.setDataFormat(
    workbook.createDataFormat()
            .getFormat("@")
);

这样创建出来的 Cell:

  • 存在;
  • 有文本格式;
  • 用户输入时不会被 Excel 自动转换。

六、EasyExcel 生命周期选择

afterCellCreate

执行太早:

text 复制代码
创建Cell
 ↓
后续写入
 ↓
样式可能被覆盖

不推荐作为最终样式设置位置。


afterCellDataConverted

适合:

已有数据导出。

例如:

java 复制代码
String code="00123";

可以设置:

java 复制代码
cellData.setType(CellDataTypeEnum.STRING);

防止数字转换。

但是:

不适合空模板。


afterCellDispose

执行最后:

text 复制代码
数据写入
 ↓
样式处理
 ↓
afterCellDispose

最适合设置:

  • 文本格式;
  • 锁定状态;
  • 最终 CellStyle。

七、最终设计

推荐:

java 复制代码
ExportSheet

 ├── maxColumnIndex
 ├── textCols
 └── protectedColumns
java 复制代码
        for (EmployeeStaffingVo vo : dataList) {
            EmployeeStaffingImportVo importVo = new EmployeeStaffingImportVo();
            BeanUtils.copyProperties(vo, importVo);
            // 项目 GUID 用于导入时按项目匹配,不展示在模板中
            importVo.setFldProjectGuid(vo.getFldProjectGuid());

            List<Object> row = new ArrayList<>();
            row.add(rowNo++);
            row.add(defaultString(importVo.getFldCompanyName()));
            row.add(defaultString(importVo.getFldProjectName()));
            row.add(defaultString(importVo.getFldProjectBusinessType()));
            row.add(defaultString(importVo.getFldBuildingArea()));
            row.add(defaultString(importVo.getFldServiceLevel()));
            dataRows.add(row);
        }

        ExportSheet exportSheet = new ExportSheet();
        exportSheet.setSheetName("人员配备统计表");
        exportSheet.setHeadList(headList);
        exportSheet.setDataList(dataRows);
        exportSheet.setCommentConfigs(commentConfigs);

        // *导入月份 列强制为文本格式,避免 Excel 把 "2026-01" 自动识别为日期
        exportSheet.setTextCols(Arrays.asList(6));
        exportSheet.setMaxColumnIndex(9);
        exportSheet.setEnhancedProtectedColumns(Arrays.asList(0, 1, 2, 3, 4, 5));

        EsEasyExcelUtil.multipleSheetExport("人员配备统计表导入模板",
                Collections.singletonList(exportSheet), response);
    }

最终效果:

列 效果
A-F 锁定
G 导入月份 文本,可输入 2026-01
H-J 可编辑

实现:

✅ 不需要 row.add("")

✅ 避免 EasyExcel 空字符串处理导致文本格式失效

✅ 不会日期自动转换

✅ 空模板支持

✅ 适用于 EasyExcel 4.0.3 导入模板生成场景

相关推荐
汉堡大王952710 小时前
一张图三句需求,我用 Trae Work 做了一块能看日出日落和月相的天文机械表
前端·后端·github
Wang's Blog10 小时前
Java 项目实战: 外卖平台优化-从库Slave配置与主从复制验证
java·开发语言
子一!!10 小时前
集成Spring家族的Spring论坛实战==一阶段
java·后端·spring
小小善后师10 小时前
用 Canvas + AI 实现登录页的 Logo 粒子动画
前端·vue.js
Amos_Web10 小时前
Rspack 源码解析(十六):多类型资源如何进入 Compilation.assets
前端·rust·前端框架
何中应10 小时前
Maven 执行控制台中文乱码问题
java·maven·intellij-idea
GAMC10 小时前
chrome-devtools-mcp:让 AI 编码助手真正"看见"浏览器
前端·人工智能
deli00710 小时前
高尔顿板:把球一颗颗丢下去,为什么最后总堆成一座钟形山
前端
Zhou14113610 小时前
SpringSecurity_01_入门与认证
java
java_nnnn10 小时前
JavaEE进阶-CSS初识
java·前端·css·java-ee·html