Java生成多份Excel发送优化:如何实现流复用并移出循环关闭?
优化Excel写入逻辑:流复用与资源管理改进
我来帮你梳理下这段代码的优化方向,主要围绕复用通用模板、简化资源管理和移出循环内重复操作这几个点来做,既能实现你想要的资源复用,也能让代码更简洁易维护:
核心优化思路
- 抽离表头模板:所有Excel的表头完全一致,没必要每次循环都重复创建。预先生成带表头的Workbook模板,每次循环直接复制模板填充数据,减少冗余操作。
- 自动资源管理:用Java 7+的
try-with-resources替代手动调用IOUtils.closeQuietly,自动关闭流和Workbook资源,既可靠又省去手动关闭的繁琐。 - 集中关闭资源:把模板Workbook的关闭操作移到循环外,只执行一次;循环内的临时Workbook在各自的作用域内关闭,避免资源泄漏。
优化后的代码实现
第一步:创建通用表头模板方法
先写一个只执行一次的方法,生成带表头的Workbook模板:
// 生成带表头的Excel模板,仅初始化一次 private XSSFWorkbook createLeaveListTemplate() { XSSFWorkbook templateWb = new XSSFWorkbook(); Sheet sheet = templateWb.createSheet("人員離調不須覆核清單"); // 批量创建表头,比逐个写更简洁 String[] headerTitles = { "離調職", "資料日期", "員編", "應用系統代號", "使用者員編", "登入帳號", "系統群組代號", "最近登入日期", "匯入日期", "註記" }; Row headerRow = sheet.createRow(0); for (int i = 0; i < headerTitles.length; i++) { headerRow.createCell(i).setCellValue(headerTitles[i]); } return templateWb; }
第二步:优化循环逻辑
把资源创建、关闭逻辑重构,移出循环内的重复操作:
// 预先初始化模板,只执行一次 XSSFWorkbook templateWorkbook = createLeaveListTemplate(); try { for (String key : manMappingSys.keySet()) { // 复制模板Workbook,直接复用表头结构 XSSFWorkbook currentWb = new XSSFWorkbook(templateWorkbook); Sheet sheet = currentWb.getSheetAt(0); int rowNum = 1; // 表头已占第0行,从第1行开始填充数据 // 填充数据逻辑(和原代码一致,仅调整起始行号) List<String> sysList = manMappingSys.get(key); for (String sysId : sysList) { List<LeaveList> leaveList = sysMappingLeave.get(sysId); for (LeaveList leave : leaveList) { Row tempRow = sheet.createRow(rowNum); tempRow.createCell(0).setCellValue(leave.getChangeLeave()); tempRow.createCell(1).setCellValue(leave.getDataDate().toString()); tempRow.createCell(2).setCellValue(leave.getUserId()); tempRow.createCell(3).setCellValue(leave.getSysNoId().getSc02ApType()); tempRow.createCell(4).setCellValue(leave.getSysNoId().getUsrId()); tempRow.createCell(5).setCellValue(leave.getSysNoId().getActNo()); tempRow.createCell(6).setCellValue(leave.getSysNoId().getRgroup()); tempRow.createCell(7).setCellValue(leave.getSysNoId().getLastDate().toString()); // 提示:原代码中"匯入日期"和"註記"列未赋值,可根据业务需求补充 rowNum++; } } // 用try-with-resources自动管理输出流,无需手动关闭 try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { currentWb.write(outputStream); BufferedInputStream inputStream = new BufferedInputStream(new ByteArrayInputStream(outputStream.toByteArray())); InputStreamResource excelResource = new InputStreamResource(inputStream, "人員離調不須覆核清單.xlsx"); InputStreamResource[] attachments = {excelResource}; // 发送邮件逻辑 SysEmployee manager = employeeMapper.getEmployeeByCn(key); String[] toMails = {manager.getMail()}; mailService.sendMail( SCHEDULE_LEAVE_OR_TRASFER_NOTICE_NOT_TEMPLATE, new HashMap(), Locale.getDefault(), toMails, attachments ); } finally { // 关闭当前循环的临时Workbook IOUtils.closeQuietly(currentWb); } } } finally { // 循环结束后关闭模板Workbook,仅执行一次 IOUtils.closeQuietly(templateWorkbook); }
优化细节解释
- 模板复用的优势:模板只创建一次,每次循环通过复制模板生成新的Workbook,避免了重复创建表头的冗余操作,尤其是当循环次数较多时,能明显提升性能。
- 资源管理改进:
- 模板Workbook的关闭操作移到最外层的
finally块,只执行一次,完全符合你“将流关闭操作移出循环”的需求。 - 循环内的输出流通过
try-with-resources自动关闭,不需要手动调用IOUtils.closeQuietly,代码更简洁且资源更安全。
- 模板Workbook的关闭操作移到最外层的
- 代码可读性提升:把表头创建逻辑抽成独立方法,循环体只关注数据填充和邮件发送,结构更清晰,后续维护或修改表头也更方便。
内容的提问来源于stack exchange,提问作者user7189675
相关产品推荐
相关产品推荐

