如何用Apache POI和Gradle从Excel生成带唯一文件名的独立Word文档
实现每行生成独立Word文档的修改方案
核心修改思路
不再复用单个Word模板文档,而是为CSV/Excel的每一行数据重新加载模板文件,替换占位符后以该行的filename字段值为文件名单独保存到输出目录。
具体修改步骤及代码
调整
merge方法逻辑- 遍历CSV每一行时,重新读取模板文件创建
XWPFDocument实例,避免前一行的修改污染后续文档 - 替换当前行的占位符后,提取
filename字段生成合法的输出路径 - 使用
try-with-resources自动关闭流和文档对象,防止资源泄漏
- 遍历CSV每一行时,重新读取模板文件创建
添加辅助工具方法
- 确保输出目录存在,避免IO异常
- 处理
filename中的非法字符,防止文件创建失败
修改后的MailMerge类代码示例:
import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVParser; import org.apache.commons.csv.CSVRecord; import org.apache.poi.xwpf.usermodel.XWPFDocument; import java.io.*; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Objects; public class MailMerge { public void merge(String templateFile, String csvFile, String outputDir) throws IOException { // 确保输出目录存在,不存在则创建 Files.createDirectories(Paths.get(outputDir)); try (Reader reader = Files.newBufferedReader(Paths.get(csvFile)); CSVParser csvParser = new CSVParser(reader, CSVFormat.DEFAULT.withHeader())) { for (CSVRecord record : csvParser) { // 为每一行重新加载模板,避免交叉污染 try (InputStream templateStream = new FileInputStream(templateFile); XWPFDocument document = new XWPFDocument(templateStream)) { // 替换当前行的占位符 replaceMarkers(document, record); // 提取并处理filename字段 String filename = sanitizeFilename(record.get("filename")); if (!filename.toLowerCase().endsWith(".docx")) { filename += ".docx"; } String outputPath = Paths.get(outputDir, filename).toString(); // 保存生成的独立文档 try (OutputStream out = new FileOutputStream(outputPath)) { document.write(out); } } } } } private void replaceMarkers(XWPFDocument document, CSVRecord record) { // 保留原有的占位符替换逻辑,确保仅修改当前文档实例 // 示例逻辑(可根据原项目实现调整): document.getParagraphs().forEach(p -> { String text = p.getText(); for (String header : record.toMap().keySet()) { String marker = String.format("${%s}", header); if (text.contains(marker)) { p.replaceText(marker, record.get(header)); } } }); // 处理表格等其他内容的占位符替换(原项目已有逻辑则保留) } // 清理文件名中的非法字符,适配多操作系统 private String sanitizeFilename(String filename) { Objects.requireNonNull(filename, "filename字段不能为空"); return filename.replaceAll("[<>:\"/\\\\|?*]", "_").trim(); } }
额外注意事项
- Excel支持:处理Excel文件时逻辑类似,使用Apache POI的
XSSFWorkbook遍历每一行,提取filename字段后执行相同的模板加载、替换、保存流程。 - 性能优化:若模板文件较大,可提前将模板内容缓存为字节数组,避免重复读取磁盘:
// 在merge方法开头缓存模板字节 byte[] templateBytes = Files.readAllBytes(Paths.get(templateFile)); // 循环中使用缓存的字节流加载模板 try (InputStream templateStream = new ByteArrayInputStream(templateBytes); XWPFDocument document = new XWPFDocument(templateStream)) { // ... 后续逻辑 } - 空值处理:可根据业务需求调整
sanitizeFilename方法,为空的filename字段设置默认文件名(如default_${行号}.docx)。 - Gradle依赖检查:确保
build.gradle中已包含必要依赖(原项目已有,若新增工具类可补充):dependencies { implementation 'org.apache.poi:poi-ooxml:5.2.5' implementation 'org.apache.commons:commons-csv:1.10.0' }
内容的提问来源于stack exchange,提问作者kheder47
相关产品推荐
相关产品推荐

