Apache POI单元格样式异常:循环内样式替换中途停止
问题背景
我继承了一段微调过的Apache POI导出Excel的代码,功能是读取ExportData对象,先写表头再逐行写入数据并设置样式。代码能正常运行,但存在一个问题:在标记为here的位置,循环执行一定次数后,自动换行样式的替换操作会突然停止,部分单元格没有应用自动换行样式,不过Excel文件能正常打开,数据无异常。
我已经通过将CellStyle声明在循环外解决了问题,但想知道最初样式中途失效的原因。补充信息:
- 通过
HSSFWorkbook.getNumCellStyles()查看,样式数约300,远低于Excel的64k样式限制,未触发相关异常 - 用
o.toString()和DataFormatter.formatCellValue(cell)验证过单元格输出符合预期 - 把样式代码移到判断语句上方,仍出现类似失效问题
原始代码
public String generateExcel(ExportData export) throws ApplicationException { String fileName = "<fileName>"; try { String exportFile = new StringBuilder().append(fileName).toString(); FileOutputStream fos = new FileOutputStream(exportFile.toString(),false); HSSFWorkbook wb = new HSSFWorkbook(); CellStyle headerStyle = <function to create header style>; HSSFSheet sheet = wb.createSheet(); wb.setSheetName(0, "Export"); int currentRow = 1; // Header Row headerRow = sheet.createRow(currentRow); currentRow++; int headerCell = 0; for (String dataId : export.getColumns()) { Cell cell = headerRow.createCell(headerCell); headerCell++; cell.setCellValue( export.getColumns().get(dataId) ); cell.setCellStyle(headerStyle); } for (BaseModel model : export.getDataList()) { Row row = sheet.createRow(currentRow); currentRow++; int currentCell = 0; for (String dataIdx : export.getColumns()) { Cell cell = row.createCell(currentCell); Object o = model.get(dataIdx); if (o instanceof Integer) { cell.setCellValue( Misc.toDouble( (Integer)o ) ); } else if (o instanceof Timestamp) { cell.setCellValue( Misc.toDate( (Timestamp) o ) ); cell.getCellStyle().setDataFormat( HSSFDataFormat.getBuiltinFormat("m/d/yy h:mm") ); } else if (o instanceof Date) { cell.setCellValue( (Date) o ); cell.getCellStyle().setDataFormat( HSSFDataFormat.getBuiltinFormat("m/d/yy") ); } else { String s = ( o != null ) ? o.toString() : ""; cell.setCellValue( s ); } // **here** - 问题所在行 HSSFCellStyle wrappedCellStyle = wb.createCellStyle(); wrappedCellStyle.setWrapText(true); cell.setCellStyle(wrappedCellStyle); currentCell++; } } for (int i = 0 ; i < headerCell ; i++) { sheet.autoSizeColumn(i, true); } wb.write(fos); fos.close(); return fileName; } catch (Exception e) { e.printStackTrace(); throw new ApplicationException(e.getMessage()); } }
问题分析
核心原因
共享样式修改导致的冲突:创建单元格时,POI会为其分配内置样式池中的默认样式。代码中调用
cell.getCellStyle().setDataFormat(...)时,直接修改了这个共享的默认样式,而非创建新样式。后续单元格会继承被修改的样式,而循环内重复创建新样式并赋值时,样式池的状态会因此混乱,导致后续样式设置被静默忽略。重复创建样式的内部优化干扰:虽然300个样式远低于64k上限,但POI对高频重复创建的相同属性样式有复用逻辑。你每次创建的
wrappedCellStyle仅设置了wrapText=true,理论上应该被复用,但之前修改共享默认样式的操作打乱了样式池的正常复用机制,最终出现部分单元格样式失效。写入阶段的样式序列化问题:在
wb.write(fos)时,POI会整理样式池并序列化。当样式池存在大量重复或冲突样式时,部分样式可能无法被正确序列化到Excel文件,导致打开后部分单元格缺失样式。
反向验证
将CellStyle移到循环外后,所有单元格共享同一个样式对象,既避免了重复创建样式的问题,也不会因修改共享默认样式产生冲突,问题得以解决,这也印证了上述分析的合理性。
内容的提问来源于stack exchange,提问作者Erik

