Apache POI导出Excel:如何将金额与对应日期列关联
问题:Excel导出时金额与日期列无法正确关联
我有一个接收动态数据并保存到Excel文件的方法,列名是用户输入的日期,能正确获取这些日期,但导出的金额无法对应到正确的日期列。当前导出效果、期望效果及原代码如下,尝试用Map实现关联但未成功,请求解决。
原代码
private void saveExcel(List<ExplosureDto> list, List<String> columns, HttpServletResponse response) throws IOException { XSSFWorkbook workbook = new XSSFWorkbook(); XSSFSheet sheet = workbook.createSheet("Reports"); XSSFRow row = sheet.createRow(0); List<Object> namesForRowZero = new ArrayList<>(columns); // Map<XSSFCell, Integer> namesForRowZero1 = new HashMap<>(); for (int i = 0; i < namesForRowZero.size(); i++) { Cell cell = row.createCell(i); cell.setCellValue(String.valueOf(namesForRowZero.get(i))); cell.setCellStyle(excelStylesDtoService.greenColor(workbook)); namesForRowZero1.put(sheet.getRow(0).getCell(i), i); } int second = sheet.getLastRowNum() + 1; for (ExplosureDto dto : list) { for (ChildExplosure childExplosure : dto.getChildExplosures()) { row = sheet.createRow(second); Cell cell1 = row.createCell(second); BigDecimal amount = childExplosure.getAmount(); sheet.setColumnWidth(second, 20 * 256); cell1.setCellValue(amount.doubleValue()); cell1.setCellStyle(excelStylesDtoService.styleBorder(workbook)); second++; } } String report = "C:/download/Report.xlsx"; FileOutputStream outputStream = new FileOutputStream(report); workbook.write(outputStream); workbook.close(); outputStream.flush(); outputStream.close(); }
当前错误导出效果
2022-01 | 2022-02 | 2022-03 | 2022-04 | 2022-05 | ------------+----------------+--------------+--------------+-------------+ | 334008,0972 | | | | | | 334008,0972 | | | | | | 334008,0972 | | | | | | 334008,0972 | | | | | | 334008,0972
期望导出效果
2022-01 | 2022-02 | 2022-03 | 2022-04 | 2022-05 | ------------+----------------+--------------+--------------+-------------+ | | 334008,0972 | 334008,0972 | 334008,0972 | | | 334008,0972 | 334008,0972 | | 334008,0972 | 334008,0972 | 334008,0972 | 334008,0972 | | | 334008,0972 | | | 334008,0972 | | | | | |
解决方案
核心问题是原代码错误地将行号作为列索引创建单元格,导致金额无法匹配日期列。正确做法是建立「日期字符串→列索引」的映射,再根据数据中的日期找到对应列填充金额。
修正后的代码
private void saveExcel(List<ExplosureDto> list, List<String> columns, HttpServletResponse response) throws IOException { XSSFWorkbook workbook = new XSSFWorkbook(); XSSFSheet sheet = workbook.createSheet("Reports"); XSSFRow headerRow = sheet.createRow(0); // 建立日期到列索引的映射(核心修正点) Map<String, Integer> dateColumnMap = new HashMap<>(); for (int i = 0; i < columns.size(); i++) { String dateStr = columns.get(i); Cell cell = headerRow.createCell(i); cell.setCellValue(dateStr); cell.setCellStyle(excelStylesDtoService.greenColor(workbook)); dateColumnMap.put(dateStr, i); // 统一设置列宽,避免重复操作 sheet.setColumnWidth(i, 20 * 256); } // 处理数据行:每个ExplosureDto对应一行 int dataRowNum = 1; for (ExplosureDto dto : list) { XSSFRow dataRow = sheet.createRow(dataRowNum); for (ChildExplosure childExplosure : dto.getChildExplosures()) { // 获取当前子项的日期,假设ChildExplosure有getDate()方法返回日期字符串 String childDate = childExplosure.getDate(); Integer colIndex = dateColumnMap.get(childDate); // 仅当日期存在于表头时填充金额 if (colIndex != null) { Cell amountCell = dataRow.createCell(colIndex); BigDecimal amount = childExplosure.getAmount(); amountCell.setCellValue(amount.doubleValue()); amountCell.setCellStyle(excelStylesDtoService.styleBorder(workbook)); } } dataRowNum++; } String report = "C:/download/Report.xlsx"; try (FileOutputStream outputStream = new FileOutputStream(report)) { workbook.write(outputStream); } finally { workbook.close(); } }
关键修正说明
- 映射关系替换:将原有的
Cell→IntegerMap改为String→IntegerMap,直接通过日期字符串定位列索引,这是关联日期与金额的核心。 - 数据遍历逻辑调整:每个
ExplosureDto对应一行数据,该行下的所有ChildExplosure根据自身日期找到对应列填充金额,符合期望的多行多列布局。 - 移除错误的列索引逻辑:删除原代码中
row.createCell(second)的错误写法,改为使用映射得到的正确列索引。 - 优化列宽设置:在表头循环中统一设置列宽,避免重复操作。
注意事项
确保ChildExplosure的日期字符串格式与表头的日期格式完全一致(例如都是yyyy-MM),否则映射会无法匹配列索引。
内容的提问来源于stack exchange,提问作者Alexander
相关产品推荐
相关产品推荐

