这里Word模板填充功能。
有两种方式实现模板填充功能,一种是借助于Aspose工具包中的Aspose.Word工具实现,另一种是借助于Deepoove工具的poi-tl提供的模板引擎实现。
一、Deepoove方式实现
API参考:http://deepoove.com/poi-tl/
借助Deepoove工具包,可以实现自动化、便捷的模板填充操作,替换规则固定,不能根据需要自定义填充规则,有时使用起来会不太方便。
1. Deepoove引入
<dependency><groupId>com.deepoove</groupId><artifactId>poi-tl</artifactId><version>1.6.0-beta1</version></dependency><!-- 可能用到的Apache poi 依赖 --><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>3.16</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml-schemas</artifactId><version>3.16</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>3.16</version></dependency>
2. 填充工具代码
import com.deepoove.poi.XWPFTemplate;import com.deepoove.poi.config.Configure;import java.io.*;import java.util.Map;/*** Word模板填充工具** @author maomaochong* @date 2021/11/26 14:21**/public class WordRenderUtil {/*** 填充模板,并导出到输出流** 填充规则:* word中模板:{{templateField}}* dataMap中:key为templateField(不带括号),value为任意待填充数据** @param templatePath 模板路径* @param dataMap 待填充数据集* @param outStream 导出输出流* @throws IOException*/public static void renderTemplateAndExportToStream(String templatePath, Map dataMap, OutputStream outStream) throws IOException {XWPFTemplate xwpfTemplate = null;InputStream inputStream = null;try {// 配置Configure config = Configure.newBuilder().build();// 读取模板到输入流inputStream = new FileInputStream(templatePath);// 编译模板,填充数据xwpfTemplate = XWPFTemplate.compile(inputStream, config).render(dataMap);// 写入到输出流xwpfTemplate.write(outStream);} catch (IOException e) {throw new RuntimeException("模板填充失败");}finally {if(xwpfTemplate != null){xwpfTemplate.close();}// 关流if (inputStream != null) {inputStream.close();}}}/*** 填充模板,并导出到指定文件** 填充规则:* word中模板:{{templateField}}* dataMap中:key为templateField(不带括号),value为任意待填充数据** @param templatePath 模板路径* @param dataMap 待填充数据集* @param destDirPath 导出目录* @param destFileName 导出文件名* @throws IOException*/public static void renderTemplateAndExportToPath(String templatePath, Map dataMap, String destDirPath, String destFileName) throws IOException {XWPFTemplate xwpfTemplate = null;InputStream inputStream = null;try{// 配置Configure config = Configure.newBuilder().build();// 读取模板到输入流inputStream = new FileInputStream(templatePath);// 编译模板,填充数据xwpfTemplate = XWPFTemplate.compile(inputStream, config).render(dataMap);// 目标目录是否存在File file = new File(destDirPath);if (!file.exists()) {file.mkdirs();}// 写入到文件xwpfTemplate.writeToFile(destDirPath + File.separator + destFileName);}catch (IOException e){throw new RuntimeException("生成文件失败!");}finally {if(xwpfTemplate != null){xwpfTemplate.close();}// 关流if (inputStream != null) {inputStream.close();}}}}
3. 测试用例
import java.io.*;import java.util.HashMap;/*** @author maomaochong* @date 2021/11/26 14:59**/public class Test02 {public static void main(String[] args) {try {// test01();test02();} catch (Exception e) {e.printStackTrace();}}private static void test01() throws Exception {HashMap<String, String> map = new HashMap<>();map.put("Code", "2021800001");map.put("Creator", "小明公司");map.put("Year", "2021");map.put("Month", "11");map.put("Day", "23");map.put("ApplyNum", "2021800001");map.put("PatentApplyNum", "2021800001");map.put("PatentName", "小明小明");map.put("Sltz", "\u2611");map.put("Bltz", "\u2610");WordRenderUtil.renderTemplateAndExportToPath(System.getProperty("user.dir") + File.separator + "template2.docx", map, System.getProperty("user.dir") + File.separator + "wordTemplate\\out", "dest.docx");}private static void test02() throws Exception {HashMap<String, String> map = new HashMap<>();map.put("Code", "2021800001");map.put("Creator", "小明公司");map.put("Year", "2021");map.put("Month", "11");map.put("Day", "23");map.put("ApplyNum", "2021800001");map.put("PatentApplyNum", "2021800001");map.put("PatentName", "小明小明");map.put("Sltz", "\u2611");map.put("Bltz", "\u2610");ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();WordRenderUtil.renderTemplateAndExportToStream(System.getProperty("user.dir") + File.separator + "template2.docx", map, byteArrayOutputStream);}}
4. 填充过程中可能会用到的一些特殊符号编码
参考:https://blog.csdn.net/cy_baicai/article/details/82897724
二、Aspose.Word方式实现
参考文档:https://apireference.aspose.com/words/java/com.aspose.words/package-summary
使用Aspose.Word工具来填充Word模板,同时可以使用其携带的工具实现从Word向Pdf的格式转换,比较方便,还可以根据需要自定义模板样式。
1. Aspose.Word引入
由于Aspose.Word是收费库,所以为了学习,从网上找到了绿色方法,仅供学习使用,我们要支持正版。
- 获取对应jar包
由于语雀不支持压缩包格式上传,将后缀改为了docx,下载后将后缀改回rar即可。
asposewordforjava15.docx
在jar包所在目录,使用如下命令将其安装到本地仓库
mvn install:install-file -Dfile=aspose-words-15.8.0-jdk16.jar -DgroupId=com.aspose -DartifactId=aspose-words -Dversion=15.8.0 -Dpackaging=jar
在pom文件里引入此依赖
<dependency><groupId>com.aspose</groupId><artifactId>aspose-words</artifactId><version>15.8.0</version></dependency><!-- 可能会用到的Apache poi依赖 --><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>3.16</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml-schemas</artifactId><version>3.16</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>3.16</version></dependency>
2. 授权文件
附件:license.xml
3. 工具类代码
import com.aspose.words.*;//import org.apache.commons.lang.StringUtils;//import org.slf4j.Logger;//import org.slf4j.LoggerFactory;import sun.misc.BASE64Decoder;import java.io.File;import java.io.FileOutputStream;import java.io.InputStream;import java.util.Base64;import java.util.HashMap;import java.util.Map;import java.util.Random;import java.util.regex.Matcher;import java.util.regex.Pattern;/*** Aspose工具类*/public class AsposeWordsUtils {// private static final Logger log = LoggerFactory.getLogger(AsposeWordsUtils.class);/*** 判断是否有授权文件 如果没有则会认为是试用版,转换的文件会有水印** @return*/public static boolean getLicense() throws Exception {boolean result = false;// 指定授权文件路径InputStream is = AsposeWordsUtils.class.getClassLoader().getResourceAsStream("license.xml");License aposeLic = new License();aposeLic.setLicense(is);result = true;return result;}/*** doc2pdf工具** @param sourcerFile 源文件* @param targetFile 目标文件*/public static void doc2pdf(String sourcerFile, String targetFile) throws Exception {if (!getLicense()) { // 验证License 若不验证则转化出的pdf文档会有水印产生return;}long old = System.currentTimeMillis();File file = new File(targetFile); // 新建一个空白pdf文档FileOutputStream os = new FileOutputStream(file);Document doc = new Document(sourcerFile); // sourcerFile是将要被转化的word文档doc.save(os, SaveFormat.PDF); // 全面支持DOC, DOCX, OOXML, RTF HTML, OpenDocument, PDF, EPUB, XPS, SWF 相互转换os.close();long now = System.currentTimeMillis();// log.info("共耗时:" + ((now - old) / 1000.0) + "秒"); //转化用时}/*** 获取Word模板文档填充工具*** 填充规则:* 模板(自定义):{{templateField}}* 数据集:key为templateField(不带括号),value为需要填充的任意数据** @param inFilePath 文档路径* @param outFilePath 输出文档路径* @param data 待替换的域的键值* @param signCode 签章图片base64编码* @return 处理结果* @throws Exception*/public static Map<String, String> readwriteWord(String inFilePath, String outFilePath, Map<Object, Object> data, String signCode) throws Exception {// 验证License 若不验证则转化出的word文档会有水印产生if (!getLicense()) {return null;}Map<String, String> map = new HashMap<>();Document doc = null;// 定义文档接口doc = new Document(inFilePath);for (Map.Entry<Object, Object> entry : data.entrySet()) {String key = entry.getKey().toString();Object value = entry.getValue();if (value != null) {Pattern p = Pattern.compile("\n");Matcher m = p.matcher(value.toString());int i = 0;String replaceValue = "";Boolean flag = false;while (m.find()) {// 换行处理replaceValue = value.toString().replace(m.group(), "" + ControlChar.LINE_BREAK + "");flag = true;}if (flag) {// 要求替换的内容是完全匹配时的替换// 可更换模板样式doc.getRange().replace("{{" + key + "}}", replaceValue, true, false);} else {// 要求替换的内容是完全匹配时的替换doc.getRange().replace("{{" + key + "}}", value.toString(), true, false);}}}if(signCode != null && !signCode.isEmpty()){DocumentBuilder builder = new DocumentBuilder(doc);builder.moveToMergeField("signature");BASE64Decoder decoder = new BASE64Decoder();byte[] imgByteArr = decoder.decodeBuffer(signCode);for (int i = 0; i < imgByteArr.length; ++i) {if (imgByteArr[i] < 0) {// 调整异常数据imgByteArr[i] += 256;}}builder.insertImage(imgByteArr, RelativeHorizontalPosition.RIGHT_MARGIN, -160, RelativeVerticalPosition.OUTSIDE_MARGIN, -140, 160, 160, WrapType.NONE);}// 生成四位随机数Random ne = new Random();int num = ne.nextInt(9999 - 1000 + 1) + 1000;String random = String.valueOf(num);String fileName = "" + System.currentTimeMillis() + random;//替换后的WordString docPath = outFilePath + fileName + inFilePath.substring(inFilePath.lastIndexOf("."));//生成的PDF位置String pdfPath = outFilePath + fileName + ".pdf";doc.save(docPath);File file = new File(docPath);if (file.exists()) {// 直接转换为worddoc2pdf(docPath, pdfPath);map.put("pdfPath", pdfPath);map.put("docPath", docPath);}return map;}}
4. 测试用例
import java.io.*;import java.util.HashMap;import java.util.Map;/*** @author maomaochong* @date 2021/11/26 14:59**/public class Test02 {public static void main(String[] args) {try {test03();} catch (Exception e) {e.printStackTrace();}}private static void test03() throws Exception {Map map = new HashMap<>();map.put("Code", "2021800001");map.put("Creator", "小明公司");map.put("Year", "2021");map.put("Month", "11");map.put("Day", "23");map.put("ApplyNum", "2021800001");map.put("PatentApplyNum", "2021800001");map.put("PatentName", "小明小明");map.put("Sltz", "\u2611");map.put("Bltz", "\u2610");AsposeWordsUtils.readwriteWord(System.getProperty("user.dir") + File.separator + "template2.docx", System.getProperty("user.dir") + File.separator + "wordTemplate\\out\\dest2.docx", map, null);}}
三、Aspose.Word动态生成Word中表格数据
直接展示多类型数据行动态生成,即两个不同类型的数据集合,在表格中动态生成的场景,单行的相对就简单一些,可以参考此种实现方式,更易处理。
1. 工具类代码
调用其中的dynamicDealTableData()方法。
//import com.alibaba.fastjson.JSONObject;import com.aspose.words.*;//import org.apache.commons.lang.StringUtils;//import org.slf4j.Logger;//import org.slf4j.LoggerFactory;import com.joezj.test.DataOne;import com.joezj.test.DataTwo;import org.apache.commons.collections4.CollectionUtils;import org.apache.commons.lang.StringUtils;import sun.misc.BASE64Decoder;import java.io.File;import java.io.FileOutputStream;import java.io.InputStream;import java.time.format.DateTimeFormatter;import java.util.*;import java.util.List;import java.util.regex.Matcher;import java.util.regex.Pattern;/*** Aspose工具类*/public class AsposeWordsUtils {// private static final Logger log = LoggerFactory.getLogger(AsposeWordsUtils.class);/*** 判断是否有授权文件 如果没有则会认为是试用版,转换的文件会有水印** @return*/public static boolean getLicense() throws Exception {boolean result = false;// 指定授权文件路径InputStream is = AsposeWordsUtils.class.getClassLoader().getResourceAsStream("license.xml");License aposeLic = new License();aposeLic.setLicense(is);result = true;return result;}/*** doc2pdf工具** @param sourcerFile 源文件* @param targetFile 目标文件*/public static void doc2pdf(String sourcerFile, String targetFile) throws Exception {if (!getLicense()) { // 验证License 若不验证则转化出的pdf文档会有水印产生return;}long old = System.currentTimeMillis();File file = new File(targetFile); // 新建一个空白pdf文档FileOutputStream os = new FileOutputStream(file);Document doc = new Document(sourcerFile); // sourcerFile是将要被转化的word文档doc.save(os, SaveFormat.PDF); // 全面支持DOC, DOCX, OOXML, RTF HTML, OpenDocument, PDF, EPUB, XPS, SWF 相互转换os.close();long now = System.currentTimeMillis();// log.info("共耗时:" + ((now - old) / 1000.0) + "秒"); //转化用时}/*** 获取Word模板文档填充工具** 填充规则:* 模板(自定义):{{templateField}}* 数据集:key为templateField(不带括号),value为需要填充的任意数据** @param inFilePath 文档路径* @param outFilePath 输出文档路径* @param data 待替换的域的键值* @param signCode 签章图片base64编码* @return 处理结果* @throws Exception*/public static Map<String, String> readwriteWord(String inFilePath, String outFilePath, Map<Object, Object> data, String signCode) throws Exception {// 验证License 若不验证则转化出的word文档会有水印产生if (!getLicense()) {return null;}Map<String, String> map = new HashMap<>();Document doc = null;// 定义文档接口doc = new Document(inFilePath);for (Map.Entry<Object, Object> entry : data.entrySet()) {String key = entry.getKey().toString();Object value = entry.getValue();if (value != null) {Pattern p = Pattern.compile("\n");Matcher m = p.matcher(value.toString());int i = 0;String replaceValue = "";Boolean flag = false;while (m.find()) {// 换行处理replaceValue = value.toString().replace(m.group(), "" + ControlChar.LINE_BREAK + "");flag = true;}if (flag) {// 要求替换的内容是完全匹配时的替换// 可更换模板样式doc.getRange().replace("{{" + key + "}}", replaceValue, true, false);} else {// 要求替换的内容是完全匹配时的替换doc.getRange().replace("{{" + key + "}}", value.toString(), true, false);}}}if(signCode != null && !signCode.isEmpty()){DocumentBuilder builder = new DocumentBuilder(doc);builder.moveToMergeField("signature");BASE64Decoder decoder = new BASE64Decoder();byte[] imgByteArr = decoder.decodeBuffer(signCode);for (int i = 0; i < imgByteArr.length; ++i) {if (imgByteArr[i] < 0) {// 调整异常数据imgByteArr[i] += 256;}}builder.insertImage(imgByteArr, RelativeHorizontalPosition.RIGHT_MARGIN, -160, RelativeVerticalPosition.OUTSIDE_MARGIN, -140, 160, 160, WrapType.NONE);}// 生成四位随机数Random ne = new Random();int num = ne.nextInt(9999 - 1000 + 1) + 1000;String random = String.valueOf(num);String fileName = "" + System.currentTimeMillis() + random;//替换后的WordString docPath = outFilePath + fileName + inFilePath.substring(inFilePath.lastIndexOf("."));//生成的PDF位置String pdfPath = outFilePath + fileName + ".pdf";doc.save(docPath);File file = new File(docPath);if (file.exists()) {// 直接转换为worddoc2pdf(docPath, pdfPath);map.put("pdfPath", pdfPath);map.put("docPath", docPath);}return map;}/*** Word中动态生成表格数据* @param inFilePath 源文件* @param outFilePath 目标文件* @param listOne 数据集1* @param listTwo 数据集2* @throws Exception*/public static void dynamicDealTableData(String inFilePath, String outFilePath, List<DataOne> listOne, List<DataTwo> listTwo) throws Exception {// 验证Licenseif (!getLicense()) {return;}DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy年MM月dd日");// 读取源文件Document doc = new Document(inFilePath);// 获取word中的第一个表格,index指定表格位置Table table = (Table) doc.getChild(NodeType.TABLE, 0, true);/*** 下边的处理方式可能比较繁琐,总之逻辑就是:* 如果数据集1有数据的话,就要根据 数据集1 的 模板行号 加上 其数据量来计算出* 数据集2 的 模板行号 再处理*/// 数据集1 模板数据首行int DataOneLineNum = 7;// 处理 数据集1if (CollectionUtils.isEmpty(listOne)) { // 数据集1 无数据// 获取模板行Range dataOneRange = table.getRows().get(DataOneLineNum).getRange();dataOneRange.replace("Index", String.valueOf(1), true, true);dataOneRange.replace("ApplyNoItem", "", true, true);dataOneRange.replace("ApplyNameItem", "", true, true);dataOneRange.replace("ApplyDateItem", "", true, true);// 数据集2 模板数据首行(无 数据集1数据)int listTwoLineNum_NonListOne = DataOneLineNum + 2;// 无 数据集2数据if (CollectionUtils.isEmpty(listTwo)) {// 获取模板行Range listTwoRange = table.getRows().get(listTwoLineNum_NonListOne).getRange();listTwoRange.replace("IndexMotel", String.valueOf(1), true, true);listTwoRange.replace("ApplyNo", "", true, true);listTwoRange.replace("ApplyDate", "", true, true);listTwoRange.replace("ApplyName", "", true, true);listTwoRange.replace("UploaderType", "", true, true);listTwoRange.replace("Remarks", "", true, true);} else {int temp = listTwoLineNum_NonListOne;for (int index = 0; index < listTwo.size(); index++) {// 复制模板行Node node = table.getRows().get(listTwoLineNum_NonListOne).deepClone(true);// 插入到第八行后table.getRows().insert(temp + 1, node);Range applyValidAmountRange = table.getRows().get(temp + 1).getRange();// 加行temp++;// 序号applyValidAmountRange.replace("IndexMotel", String.valueOf(index + 1), true, true);// 申请号if (StringUtils.isNotBlank(listTwo.get(index).getApplyNo())) {applyValidAmountRange.replace("ApplyNo", listTwo.get(index).getApplyNo(), true, true);}else{applyValidAmountRange.replace("ApplyNo", "", true, true);}// 申请日期if (Objects.nonNull(listTwo.get(index).getApplyDate())) {applyValidAmountRange.replace("ApplyDate", df.format(listTwo.get(index).getApplyDate()), true, true);}else{applyValidAmountRange.replace("ApplyDate", "", true, true);}// 名称if (StringUtils.isNotBlank(listTwo.get(index).getApplyName())) {applyValidAmountRange.replace("ApplyName", listTwo.get(index).getApplyName(), true, true);}else{applyValidAmountRange.replace("ApplyName", "", true, true);}// 类型if (StringUtils.isNotBlank(listTwo.get(index).getType())) {applyValidAmountRange.replace("UploaderType", listTwo.get(index).getType(), true, true);}else{applyValidAmountRange.replace("UploaderType", "", true, true);}// 备注if (StringUtils.isNotBlank(listTwo.get(index).getApplyRemark())) {applyValidAmountRange.replace("Remarks", listTwo.get(index).getApplyRemark(), true, true);}else{applyValidAmountRange.replace("Remarks", "", true, true);}}// 删除模板行数据table.getRows().get(listTwoLineNum_NonListOne).remove();}} else {// 处理 数据集1int temp = DataOneLineNum;for (int index = 0; index < listOne.size(); index++) {// 复制第八行Node node = table.getRows().get(DataOneLineNum).deepClone(true);// 插入到第八行后table.getRows().insert(temp + 1, node);Range range = table.getRows().get(temp + 1).getRange();// 加行temp++;// 序号range.replace("Index", String.valueOf(index + 1), true, true);// 申请号if (StringUtils.isNotBlank(listOne.get(index).getApplyNo())) {range.replace("ApplyNoItem", listOne.get(index).getApplyNo(), true, true);}else{range.replace("ApplyNoItem", "", true, true);}// 名称if (StringUtils.isNotBlank(listOne.get(index).getApplyName())) {range.replace("ApplyNameItem", listOne.get(index).getApplyName(), true, true);}else {range.replace("ApplyNameItem", "", true, true);}// 申请日期if (StringUtils.isNotBlank(df.format(listOne.get(index).getApplyDate()))) {range.replace("ApplyDateItem", df.format(listOne.get(index).getApplyDate()), true, true);} else {range.replace("ApplyDateItem", "", true, true);}}// 数据集2 数据首行(存在 数据集1)int dataTwoLineNum = DataOneLineNum + listTwo.size() + 2;// 处理 截至上一年度在审未决和有效授权专利拥有量if (CollectionUtils.isEmpty(listTwo)) {// 获取模板行Range dataTwoRange = table.getRows().get(dataTwoLineNum).getRange();dataTwoRange.replace("IndexMotel", String.valueOf(1), true, true);dataTwoRange.replace("ApplyNo", "", true, true);dataTwoRange.replace("ApplyDate", "", true, true);dataTwoRange.replace("ApplyName", "", true, true);dataTwoRange.replace("UploaderType", "", true, true);dataTwoRange.replace("Remarks", "", true, true);} else {int temp2 = dataTwoLineNum;for (int index = 0; index < listTwo.size(); index++) {// 复制模板行Node node = table.getRows().get(dataTwoLineNum).deepClone(true);// 插入到第八行后table.getRows().insert(temp2 + 1, node);Range applyValidAmountRange = table.getRows().get(temp2 + 1).getRange();// 加行temp2++;// 序号applyValidAmountRange.replace("IndexMotel", String.valueOf(index + 1), true, true);// 申请号if (StringUtils.isNotBlank(listTwo.get(index).getApplyNo())) {applyValidAmountRange.replace("ApplyNo", listTwo.get(index).getApplyNo(), true, true);}else{applyValidAmountRange.replace("ApplyNo", "", true, true);}// 申请日期if (Objects.nonNull(listTwo.get(index).getApplyDate())) {applyValidAmountRange.replace("ApplyDate", df.format(listTwo.get(index).getApplyDate()), true, true);}else{applyValidAmountRange.replace("ApplyDate", "", true, true);}// 名称if (StringUtils.isNotBlank(listTwo.get(index).getApplyName())) {applyValidAmountRange.replace("ApplyName", listTwo.get(index).getApplyName(), true, true);}else{applyValidAmountRange.replace("ApplyName", "", true, true);}// 类型if (StringUtils.isNotBlank(listTwo.get(index).getType())) {applyValidAmountRange.replace("UploaderType", listTwo.get(index).getType(), true, true);}else{applyValidAmountRange.replace("UploaderType", "", true, true);}// 备注if (StringUtils.isNotBlank(listTwo.get(index).getApplyRemark())) {applyValidAmountRange.replace("Remarks", listTwo.get(index).getApplyRemark(), true, true);}else{applyValidAmountRange.replace("Remarks", "", true, true);}}// 删除模板行数据table.getRows().get(DataOneLineNum + listTwo.size() + 2).remove();}// 删除 数据集1 模板行table.getRows().get(DataOneLineNum).remove();}// 保存到本地doc.save(outFilePath);}}
2. 测试用例
import java.io.File;import java.time.LocalDateTime;import java.util.ArrayList;import java.util.List;/*** @author maomaochong* @date 2021/11/29 10:49**/public class Test03 {public static void main(String[] args) throws Exception {test01();}private static void test01() throws Exception {List<DataOne> listOne = new ArrayList<>();List<DataTwo> listTwo = new ArrayList<>();listOne.add(new DataOne("123", "1123", LocalDateTime.now(), "ceshi", "123123"));listOne.add(new DataOne("333", "2231", LocalDateTime.now(), "ceshi2", "123113"));listTwo.add(new DataTwo("1122", "2233", "123133", LocalDateTime.now(), "测试1", "类型1", "备注"));listTwo.add(new DataTwo("3322", "1221", "123133", LocalDateTime.now(), "测试2", "类型2", "备注2"));AsposeWordsUtils.dynamicDealTableData(System.getProperty("user.dir") + File.separator + "template1.docx", System.getProperty("user.dir") + File.separator + "wordTemplate\\out\\dest3.docx", listOne, listTwo);}}
生成效果:
