java 通过模板输出word

基于java8
引入依赖
<dependency>
<groupId>org.docx4j</groupId>
<artifactId>docx4j-JAXB-ReferenceImpl</artifactId>
<version>8.3.1</version> <!-- Java 8 兼容版本 -->
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.30</version> <!-- 日志依赖 -->
</dependency>


package
com.inspur.plugins.project.export; import com.inspur.plugins.bill.dto.ProjectBillOutDTO; import com.inspur.plugins.partner.entity.PartnerFile; import org.docx4j.XmlUtils; import org.docx4j.openpackaging.exceptions.Docx4JException; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage; import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart; import org.docx4j.wml.*; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import java.io.*; import java.lang.reflect.Field; import java.math.BigDecimal; import java.math.BigInteger; import java.net.MalformedURLException; import java.net.URL; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import org.docx4j.dml.wordprocessingDrawing.Inline; // 新增:图片内联对象类 import org.docx4j.wml.Drawing; // 新增:绘图对象类 /** * Java 8 兼容的 Word 模板填充工具类 * 支持文本内容控件和表格数据填充 */ public class WordTemplateWriter { /** * 填充Word模板并输出文件 * @param templatePath 模板路径(支持类路径或绝对路径) * @param outputPath 输出文件路径 * @param dataMap 填充数据(key: 内容控件标签, value: 填充值) * @throws Exception 处理异常 */ public static void fillTemplate(String templatePath, String outputPath, Map<String, Object> dataMap) throws Exception { // 加载模板 WordprocessingMLPackage wordMLPackage = loadTemplate(templatePath); MainDocumentPart mainDocPart = wordMLPackage.getMainDocumentPart(); // 新增:判断是否为保证金模板并填充表头 boolean isDepositTemplate = templatePath.contains("(保证金)"); if (isDepositTemplate && dataMap.containsKey("payRate")) { fillTableHeader(mainDocPart, dataMap); } // 新增:替换文本占位符(<<tag>>格式) replaceTextPlaceholders(mainDocPart, dataMap); // 新增:替换图片占位符([[image:tag]]格式) replaceImagePlaceholders(mainDocPart, dataMap); // 填充表格数据(如果存在表格数据) if (dataMap.containsKey("tableData")) { fillTableData(mainDocPart, dataMap.get("tableData")); // 直接传递原始对象 } // 保存文件 saveDocument(wordMLPackage, outputPath); } /** * 填充表格表头数据(针对保证金模板特殊处理) */ private static void fillTableHeader(MainDocumentPart mainDocPart, Map<String, Object> headerData) throws Docx4JException, JAXBException { // Map<String, String> headerData = convertToMap(headerDataObj); if (headerData == null || headerData.isEmpty()) return; // 获取文档中第一个表格 List<Object> tables = mainDocPart.getJAXBNodesViaXPath("//w:tbl", false); if (tables.isEmpty()) return; Object tableObj = tables.get(0); if (tableObj instanceof JAXBElement) { tableObj = ((JAXBElement<?>) tableObj).getValue(); } Tbl table = (Tbl) tableObj; List<Tr> rows = table.getContent().stream() .filter(Tr.class::isInstance) .map(Tr.class::cast) .collect(Collectors.toList()); if (rows.isEmpty()) return; // 确保表格至少有一行 // 获取表头行(第一行) Tr headerRow = rows.get(0); List<Tc> headerCells = getCellsFromRow(headerRow); // 替换表头单元格中的 {{tag}} 占位符 replacePlaceholdersInTextNodes(extractTextNodesFromContents(headerRow.getContent()), headerData); } /** * 保证金特殊处理支持表格单元格、段落等嵌套结构)保证金特殊处理 */ private static List<Object> extractTextNodesFromContents(List<Object> content) { List<Object> textNodes = new ArrayList<>(); for (Object obj : content) { // 处理JAXBElement包装的元素 if (obj instanceof JAXBElement) { obj = ((JAXBElement<?>) obj).getValue(); } // 递归处理所有内容容器(替换原有的P和R类型判断) if (obj instanceof ContentAccessor) { // ContentAccessor是docx4j中容器元素的通用接口 textNodes.addAll(extractTextNodesFromContents(((ContentAccessor) obj).getContent())); } else if (obj instanceof Text) { // 文本节点 textNodes.add(obj); } } return textNodes; } /** * 将任意对象转换为字段名-值字符串映射(支持基本类型、String、BigDecimal等) */ private static Map<String, String> convertToMap(Object obj) { if (obj == null) return null; // 若已是Map则直接转换(兼容旧逻辑) if (obj instanceof Map) { Map<?, ?> rawMap = (Map<?, ?>) obj; Map<String, String> strMap = new HashMap<>(); rawMap.forEach((k, v) -> strMap.put(k.toString(), v != null ? v.toString() : "")); return strMap; } // 反射提取对象字段值(支持DTO对象) Map<String, String> fieldMap = new HashMap<>(); Class<?> clazz = obj.getClass(); // 获取所有字段(包括父类字段) List<Field> fields = new ArrayList<>(); while (clazz != null) { fields.addAll(Arrays.asList(clazz.getDeclaredFields())); clazz = clazz.getSuperclass(); } // 提取字段值 for (Field field : fields) { try { field.setAccessible(true); Object value = field.get(obj); fieldMap.put(field.getName(), value != null ? value.toString() : ""); } catch (Exception e) { } } return fieldMap; } /** * 加载Word模板 */ private static WordprocessingMLPackage loadTemplate(String templatePath) throws Docx4JException, IOException { // 尝试从类路径加载模板 try (InputStream is = WordTemplateWriter.class.getClassLoader().getResourceAsStream(templatePath)) { if (is != null) { return WordprocessingMLPackage.load(is); } } // 类路径加载失败则尝试绝对路径 return WordprocessingMLPackage.load(new File(templatePath)); } /** * 填充表格数据 * 模板要求:表格需包含表头行和至少一行"模板行"(内容控件作为占位符) */ private static void fillTableData(MainDocumentPart mainDocPart, Object tableDataObj) throws Docx4JException, JAXBException { // 1. 校验输入是否为列表 if (!(tableDataObj instanceof List)) { System.out.println("tableData 不是有效列表类型,跳过表格填充"); return; } List<?> tableData = (List<?>) tableDataObj; if (tableData.isEmpty()) return; // 获取文档中第一个表格 List<Object> tables = mainDocPart.getJAXBNodesViaXPath("//w:tbl", false); if (tables.isEmpty()) return; Object tableObj = tables.get(0); if (tableObj instanceof JAXBElement) { tableObj = ((JAXBElement<?>) tableObj).getValue(); } Tbl table = (Tbl) tableObj; List<Tr> rows = table.getContent().stream() .filter(Tr.class::isInstance) .map(Tr.class::cast) .collect(Collectors.toList()); if (rows.size() < 2) return; // 至少需要表头行 + 1行模板行 // 获取模板行(假设第二行为模板行) Tr templateRow = rows.get(1); // 移除模板行(后续会替换为实际数据行) rows.remove(templateRow); // 新增:计算表格总列数(从模板行获取) List<Tc> templateCells = getCellsFromRow(templateRow); int totalColumns = templateCells.size(); // 填充数据行并添加合并行 for (Object rowObj : tableData) { // 将对象转换为字段名-值映射(核心适配) Map<String, String> rowData = convertToMap(rowObj); if (rowData == null) continue; // 创建数据行 Tr dataRow = (Tr) XmlUtils.deepCopy(templateRow); fillTableRow(dataRow, rowData); // 创建要合并的第二行 Tr mergedRow = (Tr) XmlUtils.deepCopy(templateRow); clearTableRowContent(mergedRow); // 清空合并行内容 // 添加两行到表格 rows.add(dataRow); rows.add(mergedRow); // 合并两行的第一列(垂直合并) Tc dataRowFirstCell = getCellByIndex(dataRow, 0); setVerticalMerge(dataRowFirstCell, "restart"); Tc mergedRowFirstCell = getCellByIndex(mergedRow, 0); setVerticalMerge(mergedRowFirstCell, "continue"); // 给第二列赋值并横向合并后续所有列 Tc mergedRowSecondCell = getCellByIndex(mergedRow, 1); if (mergedRowSecondCell != null) { setCellContent(mergedRowSecondCell, rowData.get("calculationFormula")); // 新增:设置横向合并(合并剩余所有列) setCellGridSpan(mergedRowSecondCell, totalColumns - 1); // 新增:删除合并行中多余的单元格(从第3列开始) List<Tc> mergedRowCells = getCellsFromRow(mergedRow); if (mergedRowCells.size() > 2) { // 只保留前两列单元格,删除后续多余单元格 mergedRow.getContent().subList(2, mergedRow.getContent().size()).clear(); } } } // 更新表格内容 table.getContent().clear(); table.getContent().addAll(rows); } /** * 从行中获取所有单元格 */ private static List<Tc> getCellsFromRow(Tr row) { return row.getContent().stream() .map(obj -> { if (obj instanceof JAXBElement) { return (Tc) ((JAXBElement<?>) obj).getValue(); } else if (obj instanceof Tc) { return (Tc) obj; } return null; }) .filter(Objects::nonNull) .collect(Collectors.toList()); } /** * 设置单元格横向合并(跨列) * @param cell 单元格 * @param span 合并列数(>=2表示合并后续span-1列) */ private static void setCellGridSpan(Tc cell, int span) { if (cell == null || span <= 1) return; // 无需合并或参数无效 TcPr tcPr = cell.getTcPr(); if (tcPr == null) { tcPr = new TcPr(); cell.setTcPr(tcPr); } // 设置横向合并属性(gridSpan表示当前单元格跨越的列数) TcPrInner.GridSpan gridSpan = new TcPrInner.GridSpan(); gridSpan.setVal(BigInteger.valueOf(span)); tcPr.setGridSpan(gridSpan); } /** * 填充表格行数据 */ private static void fillTableRow(Tr row, Map<String, String> rowData) { List<Tc> cells = row.getContent().stream() // 新增:处理 JAXBElement 包装的单元格 .map(obj -> { if (obj instanceof JAXBElement) { return ((JAXBElement<?>) obj).getValue(); } return obj; }) .filter(Tc.class::isInstance) .map(Tc.class::cast) .collect(Collectors.toList()); for (Tc cell : cells) { // 提取单元格内所有文本节点 List<Object> cellTextNodes = extractTextNodesFromContent(cell.getContent()); // 新增:合并多个文本节点为一个(若存在) if (cellTextNodes.size() > 1) { // 1. 合并所有文本节点内容 StringBuilder mergedText = new StringBuilder(); for (Object node : cellTextNodes) { if (node instanceof Text) { mergedText.append(((Text) node).getValue()); } } // 2. 清空单元格原有内容,添加合并后的文本节点 cell.getContent().clear(); P paragraph = new P(); R run = new R(); Text mergedTextNode = new Text(); mergedTextNode.setValue(mergedText.toString()); run.getContent().add(mergedTextNode); paragraph.getContent().add(run); cell.getContent().add(paragraph); // 3. 更新文本节点列表为合并后的单个节点 cellTextNodes = Collections.singletonList(mergedTextNode); } // 替换单元格内的 {{tag}} 占位符 replacePlaceholdersInTextNodes(cellTextNodes, rowData); } } /** * 通用占位符替换工具(供文本和表格共用) * 替换文本节点列表中的 {{tag}} 占位符 */ private static void replacePlaceholdersInTextNodes(List<Object> textNodes, Map<String, ?> dataMap) { Pattern placeholderPattern = Pattern.compile("\\[\\[(.+?)\\]\\]"); for (Object obj : textNodes) { Text textNode = null; // 处理JAXBElement包装的文本节点 if (obj instanceof JAXBElement) { JAXBElement<?> jaxbElement = (JAXBElement<?>) obj; if (jaxbElement.getValue() instanceof Text) { textNode = (Text) jaxbElement.getValue(); } } else if (obj instanceof Text) { textNode = (Text) obj; } if (textNode != null) { String originalText = textNode.getValue(); Matcher matcher = placeholderPattern.matcher(originalText); StringBuffer replacedText = new StringBuffer(); // 替换所有匹配的占位符 while (matcher.find()) { String tag = matcher.group(1); Object value = dataMap.get(tag); String replacement = value != null ? value.toString() : ""; matcher.appendReplacement(replacedText, Matcher.quoteReplacement(replacement)); } matcher.appendTail(replacedText); textNode.setValue(replacedText.toString()); } } } /** * 从内容中递归提取所有文本节点(支持表格单元格、段落等嵌套结构) */ private static List<Object> extractTextNodesFromContent(List<Object> content) { List<Object> textNodes = new ArrayList<>(); for (Object obj : content) { // 处理JAXBElement包装的元素 if (obj instanceof JAXBElement) { obj = ((JAXBElement<?>) obj).getValue(); } // 递归处理段落(P)和文本块(R) if (obj instanceof P) { // 段落 textNodes.addAll(extractTextNodesFromContent(((P) obj).getContent())); } else if (obj instanceof R) { // 文本块 textNodes.addAll(extractTextNodesFromContent(((R) obj).getContent())); } else if (obj instanceof Text) { // 文本节点 textNodes.add(obj); } } return textNodes; } /** * 替换文档中的文本占位符(格式:<<tag>>) * 支持占位符前后有其他文本的场景 */ private static void replaceTextPlaceholders(MainDocumentPart mainDocPart, Map<String, Object> dataMap) throws Docx4JException, JAXBException { // 1. 获取文档中所有文本节点(<w:t>元素) List<Object> textNodes = mainDocPart.getJAXBNodesViaXPath("//w:t", false); // 2. 定义占位符匹配模式({{tag}}格式) Pattern placeholderPattern = Pattern.compile("\\<\\<(.+?)\\>\\>"); for (Object obj : textNodes) { Text textNode = null; // 处理可能的JAXBElement包装节点 if (obj instanceof JAXBElement) { JAXBElement<?> jaxbElement = (JAXBElement<?>) obj; if (jaxbElement.getValue() instanceof Text) { textNode = (Text) jaxbElement.getValue(); } } else if (obj instanceof Text) { textNode = (Text) obj; } if (textNode != null) { String originalText = textNode.getValue(); Matcher matcher = placeholderPattern.matcher(originalText); StringBuffer replacedText = new StringBuffer(); // 3. 匹配并替换所有占位符 while (matcher.find()) { String tag = matcher.group(1); // 获取占位符中的tag(如{{projectName}}中的projectName) Object value = dataMap.get(tag); String replacement = value != null ? value.toString() : ""; // 无数据时替换为空字符串 matcher.appendReplacement(replacedText, Matcher.quoteReplacement(replacement)); } matcher.appendTail(replacedText); // 拼接剩余文本 // 4. 更新文本节点值 textNode.setValue(replacedText.toString()); } } } /** * 从行中获取指定索引的单元格 */ private static Tc getCellByIndex(Tr row, int index) { List<Tc> cells = row.getContent().stream() .map(obj -> { if (obj instanceof JAXBElement) { return (Tc) ((JAXBElement<?>) obj).getValue(); } else if (obj instanceof Tc) { return (Tc) obj; } return null; }) .filter(Objects::nonNull) .collect(Collectors.toList()); return cells.size() > index ? cells.get(index) : null; } /** * 设置单元格垂直合并属性 * @param cell 单元格 * @param val "restart" 开始合并, "continue" 继续合并 */ private static void setVerticalMerge(Tc cell, String val) { if (cell == null) return; TcPr tcPr = cell.getTcPr(); if (tcPr == null) { tcPr = new TcPr(); cell.setTcPr(tcPr); } TcPrInner.VMerge vMerge = new TcPrInner.VMerge(); vMerge.setVal(val); tcPr.setVMerge(vMerge); } /** * 清空行中所有单元格的内容 */ private static void clearTableRowContent(Tr row) { List<Tc> cells = row.getContent().stream() .map(obj -> { if (obj instanceof JAXBElement) { return (Tc) ((JAXBElement<?>) obj).getValue(); } else if (obj instanceof Tc) { return (Tc) obj; } return null; }) .filter(Objects::nonNull) .collect(Collectors.toList()); for (Tc cell : cells) { cell.getContent().clear(); } } /** * 设置单元格内容 */ private static void setCellContent(Tc cell, String content) { if (cell == null) return; cell.getContent().clear(); P paragraph = new P(); R run = new R(); Text text = new Text(); text.setValue(content != null ? content : ""); run.getContent().add(text); paragraph.getContent().add(run); cell.getContent().add(paragraph); } /** * 保存文档到指定路径 */ private static void saveDocument(WordprocessingMLPackage wordMLPackage, String outputPath) throws Docx4JException, IOException { File outputFile = new File(outputPath); // 确保父目录存在 if (outputFile.getParentFile() != null) { outputFile.getParentFile().mkdirs(); } try (FileOutputStream fos = new FileOutputStream(outputFile)) { wordMLPackage.save(fos); } } // 新增:图片插入相关方法开始 /** * 替换文档中的图片占位符(格式:[[image:tag]]) */ private static void replaceImagePlaceholders(MainDocumentPart mainDocPart, Map<String, Object> dataMap) throws Exception { // 获取所有段落 List<Object> paragraphs = mainDocPart.getJAXBNodesViaXPath("//w:p", false); for (Object paraObj : paragraphs) { P paragraph = (P) paraObj; List<Object> content = paragraph.getContent(); // 遍历段落内容查找图片占位符 for (int i = 0; i < content.size(); i++) { Object obj = content.get(i); if (obj instanceof JAXBElement) { obj = ((JAXBElement<?>) obj).getValue(); } if (obj instanceof R) { // 文本块 R run = (R) obj; List<Object> runContent = run.getContent(); for (int j = 0; j < runContent.size(); j++) { Object runObj = runContent.get(j); // 新增:处理 JAXBElement 包装的文本节点(关键修复) if (runObj instanceof JAXBElement) { runObj = ((JAXBElement<?>) runObj).getValue(); // 提取包装的实际值 } if (runObj instanceof Text) { // 现在能正确识别被包装的 Text 节点 Text textNode = (Text) runObj; String text = textNode.getValue(); // 匹配图片占位符格式 {{image:tag}} Pattern pattern = Pattern.compile("\\[\\[image:(.+?)\\]\\]"); Matcher matcher = pattern.matcher(text); if (matcher.find()) { String imageTag = matcher.group(1); Object imageObj = dataMap.get(imageTag); // 不再强制转换为String,支持List if (imageObj == null) continue; // 1. 移除原始占位符文本节点 runContent.remove(j); // 2. 统一处理单图片(String)和多图片(List<String>) List<String> imagePaths = new ArrayList<>(); if (imageObj instanceof List) { imagePaths = (List<String>) imageObj; // 多图片列表 } else if (imageObj instanceof String) { imagePaths.add((String) imageObj); // 兼容单图片字符串 } // 3. 循环插入所有图片 for (String imgPath : imagePaths) { Drawing drawing = createImageDrawing(mainDocPart, imgPath, 400, 300); runContent.add(j++, drawing); // 逐个添加图片,索引递增 } // 4. 处理占位符前后的剩余文本(移至所有图片之后) String remainingText = text.replace(matcher.group(0), ""); if (!remainingText.isEmpty()) { Text remainingTextNode = new Text(); remainingTextNode.setValue(remainingText); runContent.add(j, remainingTextNode); } } } } } } } } /** * 创建图片Drawing对象(适配 docx4j-core-8.3.1) */ private static Drawing createImageDrawing(MainDocumentPart mainDocPart, String imagePath, int width, int height) throws Exception { // 1. 读取图片文件(Java 8 兼容方式) byte[] imageBytes; try (InputStream fis = getInputStreamByPath(imagePath); ByteArrayOutputStream bos = new ByteArrayOutputStream()) { byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = fis.read(buffer)) != -1) { bos.write(buffer, 0, bytesRead); } imageBytes = bos.toByteArray(); } // 2. 使用 docx4j 8.3.1 推荐的工具类创建图片部件 BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart( mainDocPart.getPackage(), // 当前文档包 mainDocPart, // 源部件(主文档) imageBytes // 图片字节数组 ); // 3. 计算图片尺寸(1px = 1440 EMUs,Word 内部单位) long emuWidth = width * 1440L; long emuHeight = height * 1440L; // 4. 创建内联图片对象(适配 8.3.1 版本参数要求) Inline inline = imagePart.createImageInline( "image", // 文件名提示 "alt text", // 替代文本 0, // 图片ID(整数) 1, // 图片索引(整数) emuWidth, // 宽度(EMU) emuHeight, // 高度(EMU) false // 是否链接(false=嵌入) ); // 5. 包装为Drawing对象返回 Drawing drawing = new Drawing(); drawing.getAnchorOrInline().add(inline); return drawing; } private static InputStream getInputStreamByPath(String path) throws IOException, MalformedURLException { if (path.startsWith("http://") || path.startsWith("https://")) { // 网络URL:使用URL.openStream() URL url = new URL(path); return url.openStream(); } else { // 本地文件:使用FileInputStream(保持原逻辑) return new FileInputStream(path); } } /** * 生成Word文档 * @param dataMap 数据模型 * @param outPath 输出路径 * @throws Exception */ public static void outPutWordFile(Map<String, Object> dataMap, String outPath) throws Exception { try { // 根据条件动态确定模板文件名(resources/template目录下的文件名) String templateFileName = ""; String cooperationWay = (String) dataMap.get("cooperationWay"); Integer isDeposit = (Integer) dataMap.get("isDeposit"); // 拼接模板文件名(根据实际模板文件命名调整) if ("渠道合作".equals(cooperationWay)) { templateFileName = isDeposit == 1 ? "【充电渠道费】计算说明(保证金).docx" : "【充电渠道费】计算说明.docx"; } else if ("设施租赁".equals(cooperationWay)) { templateFileName = "【充电设施租赁费】计算说明.docx"; } else { templateFileName = "【充电场地管理服务费】计算说明.docx"; } // 构建 resources/template 目录下的模板资源路径(类路径相对路径) String templateResourcePath = "template/" + templateFileName; // 校验模板是否存在于 resources/template 目录 ClassLoader classLoader = WordTemplateWriter.class.getClassLoader(); if (classLoader.getResource(templateResourcePath) == null) { throw new FileNotFoundException("模板文件不存在于 resources/template: " + templateResourcePath); } // 2. 填充模板(直接传入类路径下的模板资源路径) // ... 现有代码 ... // 2. 填充模板(直接传入类路径下的模板资源路径) fillTemplate( templateResourcePath, // 类路径下的模板路径:resources/template/xxx.docx outPath + File.separator + templateFileName, // 使用跨平台路径分隔符 dataMap ); // ... 现有代码 ... System.out.println("Word文档生成成功!");} catch (Exception e) { e.printStackTrace(); } } // 测试方法 // public static void main(String[] args) { // try { // // 1. 准备数据 // Map<String, Object> dataMap = new HashMap<>(); // // 文本数据 // dataMap.put("cooperationWay","设施租赁"); // // isDeposit 1涉及保证金 0不涉及保证金 // dataMap.put("isDeposit",1); // // 最终支付比例,只有涉及保证金才有这个字段 // dataMap.put("payRate","95%"); // dataMap.put("contractCode","contractCode1"); // dataMap.put("calculationBaseName","测试计算基准名称"); // dataMap.put("billMonth","2025-09、2025-08、2025-07"); // dataMap.put("billingModel","用户消费金额(不含税)*考核利用率系数*考评得分系数*(1-保证金预留比例)"); // String outPath = "C:\\Users\\sombo\\Desktop\\"; // // 根据条件动态确定模板文件名(resources/template目录下的文件名) // String templateFileName = ""; // String cooperationWay = (String) dataMap.get("cooperationWay"); // Integer isDeposit = (Integer) dataMap.get("isDeposit"); // // // 拼接模板文件名(根据实际模板文件命名调整) // if ("渠道合作".equals(cooperationWay)) { // templateFileName = isDeposit == 1 ? "【充电渠道费】计算说明(保证金).docx" : "【充电渠道费】计算说明.docx"; // } else if ("设施租赁".equals(cooperationWay)) { // templateFileName = "【充电设施租赁费】计算说明.docx"; // } else { // templateFileName = "【充电场地管理服务费】计算说明.docx"; // } // // // 构建 resources/template 目录下的模板资源路径(类路径相对路径) // String templateResourcePath = "template/" + templateFileName; // // // 校验模板是否存在于 resources/template 目录 // ClassLoader classLoader = WordTemplateWriter.class.getClassLoader(); // if (classLoader.getResource(templateResourcePath) == null) { // throw new FileNotFoundException("模板文件不存在于 resources/template: " + templateResourcePath); // } // // // 表格数据(需与模板中表格控件标签对应) // List<ProjectBillOutDTO> tableData = new ArrayList<>(); // ProjectBillOutDTO row1 = new ProjectBillOutDTO(); // row1.setStaMonth("2023-10"); // row1.setCalculationBaseAmount(new BigDecimal("1000000")); // row1.setCalculationBaseName("端口"); // row1.setCalculationFormula("1000000*95%*100%*(1-0.1)"); // tableData.add(row1); // ProjectBillOutDTO row2 = new ProjectBillOutDTO(); // row2.setStaMonth("2023-11"); // row2.setCalculationBaseAmount(new BigDecimal("1000000")); // row2.setCalculationBaseName("端口"); // row2.setCalculationFormula("1000*95%*100%*(1-0.1)"); // tableData.add(row2); // dataMap.put("tableData", tableData); // // 新增:图片数据(key对应模板中的[[image:tag]]) // dataMap.put("projectLogo", Arrays.asList( // "http://10.110.141.88:8855/scap/others/2025/7/23/%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_20250430104053_1753232919287.png", // "http://10.110.141.88:8855/scap/others/2025/7/23/%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_20250430104053_1753232919287.png", // "http://10.110.141.88:8855/scap/others/2025/7/23/%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_20250430104053_1753232919287.png" // )); // fillTemplate( // templateResourcePath, // 类路径下的模板路径:resources/template/xxx.docx // outPath+templateFileName, // 输出路径 // dataMap // ); // System.out.println("Word文档生成成功!"); // } catch (Exception e) { // e.printStackTrace(); // } // } }

 

posted @ 2025-09-17 11:26  无情风中  阅读(17)  评论(0)    收藏  举报