这里Word模板填充功能。

有两种方式实现模板填充功能,一种是借助于Aspose工具包中的Aspose.Word工具实现,另一种是借助于Deepoove工具的poi-tl提供的模板引擎实现。

一、Deepoove方式实现

API参考:http://deepoove.com/poi-tl/

借助Deepoove工具包,可以实现自动化、便捷的模板填充操作,替换规则固定,不能根据需要自定义填充规则,有时使用起来会不太方便。

1. Deepoove引入

  1. <dependency>
  2. <groupId>com.deepoove</groupId>
  3. <artifactId>poi-tl</artifactId>
  4. <version>1.6.0-beta1</version>
  5. </dependency>
  6. <!-- 可能用到的Apache poi 依赖 -->
  7. <dependency>
  8. <groupId>org.apache.poi</groupId>
  9. <artifactId>poi</artifactId>
  10. <version>3.16</version>
  11. </dependency>
  12. <dependency>
  13. <groupId>org.apache.poi</groupId>
  14. <artifactId>poi-ooxml-schemas</artifactId>
  15. <version>3.16</version>
  16. </dependency>
  17. <dependency>
  18. <groupId>org.apache.poi</groupId>
  19. <artifactId>poi-ooxml</artifactId>
  20. <version>3.16</version>
  21. </dependency>

2. 填充工具代码

  1. import com.deepoove.poi.XWPFTemplate;
  2. import com.deepoove.poi.config.Configure;
  3. import java.io.*;
  4. import java.util.Map;
  5. /**
  6. * Word模板填充工具
  7. *
  8. * @author maomaochong
  9. * @date 2021/11/26 14:21
  10. **/
  11. public class WordRenderUtil {
  12. /**
  13. * 填充模板,并导出到输出流
  14. *
  15. * 填充规则:
  16. * word中模板:{{templateField}}
  17. * dataMap中:key为templateField(不带括号),value为任意待填充数据
  18. *
  19. * @param templatePath 模板路径
  20. * @param dataMap 待填充数据集
  21. * @param outStream 导出输出流
  22. * @throws IOException
  23. */
  24. public static void renderTemplateAndExportToStream(String templatePath, Map dataMap, OutputStream outStream) throws IOException {
  25. XWPFTemplate xwpfTemplate = null;
  26. InputStream inputStream = null;
  27. try {
  28. // 配置
  29. Configure config = Configure.newBuilder().build();
  30. // 读取模板到输入流
  31. inputStream = new FileInputStream(templatePath);
  32. // 编译模板,填充数据
  33. xwpfTemplate = XWPFTemplate
  34. .compile(inputStream, config)
  35. .render(dataMap);
  36. // 写入到输出流
  37. xwpfTemplate.write(outStream);
  38. } catch (IOException e) {
  39. throw new RuntimeException("模板填充失败");
  40. }finally {
  41. if(xwpfTemplate != null){
  42. xwpfTemplate.close();
  43. }
  44. // 关流
  45. if (inputStream != null) {
  46. inputStream.close();
  47. }
  48. }
  49. }
  50. /**
  51. * 填充模板,并导出到指定文件
  52. *
  53. * 填充规则:
  54. * word中模板:{{templateField}}
  55. * dataMap中:key为templateField(不带括号),value为任意待填充数据
  56. *
  57. * @param templatePath 模板路径
  58. * @param dataMap 待填充数据集
  59. * @param destDirPath 导出目录
  60. * @param destFileName 导出文件名
  61. * @throws IOException
  62. */
  63. public static void renderTemplateAndExportToPath(String templatePath, Map dataMap, String destDirPath, String destFileName) throws IOException {
  64. XWPFTemplate xwpfTemplate = null;
  65. InputStream inputStream = null;
  66. try{
  67. // 配置
  68. Configure config = Configure.newBuilder().build();
  69. // 读取模板到输入流
  70. inputStream = new FileInputStream(templatePath);
  71. // 编译模板,填充数据
  72. xwpfTemplate = XWPFTemplate
  73. .compile(inputStream, config)
  74. .render(dataMap);
  75. // 目标目录是否存在
  76. File file = new File(destDirPath);
  77. if (!file.exists()) {
  78. file.mkdirs();
  79. }
  80. // 写入到文件
  81. xwpfTemplate.writeToFile(destDirPath + File.separator + destFileName);
  82. }catch (IOException e){
  83. throw new RuntimeException("生成文件失败!");
  84. }finally {
  85. if(xwpfTemplate != null){
  86. xwpfTemplate.close();
  87. }
  88. // 关流
  89. if (inputStream != null) {
  90. inputStream.close();
  91. }
  92. }
  93. }
  94. }

3. 测试用例

模板:
template2.docx

  1. import java.io.*;
  2. import java.util.HashMap;
  3. /**
  4. * @author maomaochong
  5. * @date 2021/11/26 14:59
  6. **/
  7. public class Test02 {
  8. public static void main(String[] args) {
  9. try {
  10. // test01();
  11. test02();
  12. } catch (Exception e) {
  13. e.printStackTrace();
  14. }
  15. }
  16. private static void test01() throws Exception {
  17. HashMap<String, String> map = new HashMap<>();
  18. map.put("Code", "2021800001");
  19. map.put("Creator", "小明公司");
  20. map.put("Year", "2021");
  21. map.put("Month", "11");
  22. map.put("Day", "23");
  23. map.put("ApplyNum", "2021800001");
  24. map.put("PatentApplyNum", "2021800001");
  25. map.put("PatentName", "小明小明");
  26. map.put("Sltz", "\u2611");
  27. map.put("Bltz", "\u2610");
  28. WordRenderUtil.renderTemplateAndExportToPath(System.getProperty("user.dir") + File.separator + "template2.docx", map, System.getProperty("user.dir") + File.separator + "wordTemplate\\out", "dest.docx");
  29. }
  30. private static void test02() throws Exception {
  31. HashMap<String, String> map = new HashMap<>();
  32. map.put("Code", "2021800001");
  33. map.put("Creator", "小明公司");
  34. map.put("Year", "2021");
  35. map.put("Month", "11");
  36. map.put("Day", "23");
  37. map.put("ApplyNum", "2021800001");
  38. map.put("PatentApplyNum", "2021800001");
  39. map.put("PatentName", "小明小明");
  40. map.put("Sltz", "\u2611");
  41. map.put("Bltz", "\u2610");
  42. ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
  43. WordRenderUtil.renderTemplateAndExportToStream(System.getProperty("user.dir") + File.separator + "template2.docx", map, byteArrayOutputStream);
  44. }
  45. }

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是收费库,所以为了学习,从网上找到了绿色方法,仅供学习使用,我们要支持正版。

  1. 获取对应jar包

由于语雀不支持压缩包格式上传,将后缀改为了docx,下载后将后缀改回rar即可。
asposewordforjava15.docx

  1. 在jar包所在目录,使用如下命令将其安装到本地仓库

    1. mvn install:install-file -Dfile=aspose-words-15.8.0-jdk16.jar -DgroupId=com.aspose -DartifactId=aspose-words -Dversion=15.8.0 -Dpackaging=jar
  2. 在pom文件里引入此依赖

    1. <dependency>
    2. <groupId>com.aspose</groupId>
    3. <artifactId>aspose-words</artifactId>
    4. <version>15.8.0</version>
    5. </dependency>
    6. <!-- 可能会用到的Apache poi依赖 -->
    7. <dependency>
    8. <groupId>org.apache.poi</groupId>
    9. <artifactId>poi</artifactId>
    10. <version>3.16</version>
    11. </dependency>
    12. <dependency>
    13. <groupId>org.apache.poi</groupId>
    14. <artifactId>poi-ooxml-schemas</artifactId>
    15. <version>3.16</version>
    16. </dependency>
    17. <dependency>
    18. <groupId>org.apache.poi</groupId>
    19. <artifactId>poi-ooxml</artifactId>
    20. <version>3.16</version>
    21. </dependency>

2. 授权文件

附件:license.xml

3. 工具类代码

  1. import com.aspose.words.*;
  2. //import org.apache.commons.lang.StringUtils;
  3. //import org.slf4j.Logger;
  4. //import org.slf4j.LoggerFactory;
  5. import sun.misc.BASE64Decoder;
  6. import java.io.File;
  7. import java.io.FileOutputStream;
  8. import java.io.InputStream;
  9. import java.util.Base64;
  10. import java.util.HashMap;
  11. import java.util.Map;
  12. import java.util.Random;
  13. import java.util.regex.Matcher;
  14. import java.util.regex.Pattern;
  15. /**
  16. * Aspose工具类
  17. */
  18. public class AsposeWordsUtils {
  19. // private static final Logger log = LoggerFactory.getLogger(AsposeWordsUtils.class);
  20. /**
  21. * 判断是否有授权文件 如果没有则会认为是试用版,转换的文件会有水印
  22. *
  23. * @return
  24. */
  25. public static boolean getLicense() throws Exception {
  26. boolean result = false;
  27. // 指定授权文件路径
  28. InputStream is = AsposeWordsUtils.class.getClassLoader().getResourceAsStream("license.xml");
  29. License aposeLic = new License();
  30. aposeLic.setLicense(is);
  31. result = true;
  32. return result;
  33. }
  34. /**
  35. * doc2pdf工具
  36. *
  37. * @param sourcerFile 源文件
  38. * @param targetFile 目标文件
  39. */
  40. public static void doc2pdf(String sourcerFile, String targetFile) throws Exception {
  41. if (!getLicense()) { // 验证License 若不验证则转化出的pdf文档会有水印产生
  42. return;
  43. }
  44. long old = System.currentTimeMillis();
  45. File file = new File(targetFile); // 新建一个空白pdf文档
  46. FileOutputStream os = new FileOutputStream(file);
  47. Document doc = new Document(sourcerFile); // sourcerFile是将要被转化的word文档
  48. doc.save(os, SaveFormat.PDF); // 全面支持DOC, DOCX, OOXML, RTF HTML, OpenDocument, PDF, EPUB, XPS, SWF 相互转换
  49. os.close();
  50. long now = System.currentTimeMillis();
  51. // log.info("共耗时:" + ((now - old) / 1000.0) + "秒"); //转化用时
  52. }
  53. /**
  54. * 获取Word模板文档填充工具
  55. *
  56. *
  57. * 填充规则:
  58. * 模板(自定义):{{templateField}}
  59. * 数据集:key为templateField(不带括号),value为需要填充的任意数据
  60. *
  61. * @param inFilePath 文档路径
  62. * @param outFilePath 输出文档路径
  63. * @param data 待替换的域的键值
  64. * @param signCode 签章图片base64编码
  65. * @return 处理结果
  66. * @throws Exception
  67. */
  68. public static Map<String, String> readwriteWord(String inFilePath, String outFilePath, Map<Object, Object> data, String signCode) throws Exception {
  69. // 验证License 若不验证则转化出的word文档会有水印产生
  70. if (!getLicense()) {
  71. return null;
  72. }
  73. Map<String, String> map = new HashMap<>();
  74. Document doc = null;
  75. // 定义文档接口
  76. doc = new Document(inFilePath);
  77. for (Map.Entry<Object, Object> entry : data.entrySet()) {
  78. String key = entry.getKey().toString();
  79. Object value = entry.getValue();
  80. if (value != null) {
  81. Pattern p = Pattern.compile("\n");
  82. Matcher m = p.matcher(value.toString());
  83. int i = 0;
  84. String replaceValue = "";
  85. Boolean flag = false;
  86. while (m.find()) {
  87. // 换行处理
  88. replaceValue = value.toString().replace(m.group(), "" + ControlChar.LINE_BREAK + "");
  89. flag = true;
  90. }
  91. if (flag) {
  92. // 要求替换的内容是完全匹配时的替换
  93. // 可更换模板样式
  94. doc.getRange().replace("{{" + key + "}}", replaceValue, true, false);
  95. } else {
  96. // 要求替换的内容是完全匹配时的替换
  97. doc.getRange().replace("{{" + key + "}}", value.toString(), true, false);
  98. }
  99. }
  100. }
  101. if(signCode != null && !signCode.isEmpty()){
  102. DocumentBuilder builder = new DocumentBuilder(doc);
  103. builder.moveToMergeField("signature");
  104. BASE64Decoder decoder = new BASE64Decoder();
  105. byte[] imgByteArr = decoder.decodeBuffer(signCode);
  106. for (int i = 0; i < imgByteArr.length; ++i) {
  107. if (imgByteArr[i] < 0) {// 调整异常数据
  108. imgByteArr[i] += 256;
  109. }
  110. }
  111. builder.insertImage(imgByteArr, RelativeHorizontalPosition.RIGHT_MARGIN, -160, RelativeVerticalPosition.OUTSIDE_MARGIN, -140, 160, 160, WrapType.NONE);
  112. }
  113. // 生成四位随机数
  114. Random ne = new Random();
  115. int num = ne.nextInt(9999 - 1000 + 1) + 1000;
  116. String random = String.valueOf(num);
  117. String fileName = "" + System.currentTimeMillis() + random;
  118. //替换后的Word
  119. String docPath = outFilePath + fileName + inFilePath.substring(inFilePath.lastIndexOf("."));
  120. //生成的PDF位置
  121. String pdfPath = outFilePath + fileName + ".pdf";
  122. doc.save(docPath);
  123. File file = new File(docPath);
  124. if (file.exists()) {
  125. // 直接转换为word
  126. doc2pdf(docPath, pdfPath);
  127. map.put("pdfPath", pdfPath);
  128. map.put("docPath", docPath);
  129. }
  130. return map;
  131. }
  132. }

4. 测试用例

模板:template2.docx

  1. import java.io.*;
  2. import java.util.HashMap;
  3. import java.util.Map;
  4. /**
  5. * @author maomaochong
  6. * @date 2021/11/26 14:59
  7. **/
  8. public class Test02 {
  9. public static void main(String[] args) {
  10. try {
  11. test03();
  12. } catch (Exception e) {
  13. e.printStackTrace();
  14. }
  15. }
  16. private static void test03() throws Exception {
  17. Map map = new HashMap<>();
  18. map.put("Code", "2021800001");
  19. map.put("Creator", "小明公司");
  20. map.put("Year", "2021");
  21. map.put("Month", "11");
  22. map.put("Day", "23");
  23. map.put("ApplyNum", "2021800001");
  24. map.put("PatentApplyNum", "2021800001");
  25. map.put("PatentName", "小明小明");
  26. map.put("Sltz", "\u2611");
  27. map.put("Bltz", "\u2610");
  28. AsposeWordsUtils.readwriteWord(System.getProperty("user.dir") + File.separator + "template2.docx", System.getProperty("user.dir") + File.separator + "wordTemplate\\out\\dest2.docx", map, null);
  29. }
  30. }

三、Aspose.Word动态生成Word中表格数据

直接展示多类型数据行动态生成,即两个不同类型的数据集合,在表格中动态生成的场景,单行的相对就简单一些,可以参考此种实现方式,更易处理。
image.png

1. 工具类代码

调用其中的dynamicDealTableData()方法。

  1. //import com.alibaba.fastjson.JSONObject;
  2. import com.aspose.words.*;
  3. //import org.apache.commons.lang.StringUtils;
  4. //import org.slf4j.Logger;
  5. //import org.slf4j.LoggerFactory;
  6. import com.joezj.test.DataOne;
  7. import com.joezj.test.DataTwo;
  8. import org.apache.commons.collections4.CollectionUtils;
  9. import org.apache.commons.lang.StringUtils;
  10. import sun.misc.BASE64Decoder;
  11. import java.io.File;
  12. import java.io.FileOutputStream;
  13. import java.io.InputStream;
  14. import java.time.format.DateTimeFormatter;
  15. import java.util.*;
  16. import java.util.List;
  17. import java.util.regex.Matcher;
  18. import java.util.regex.Pattern;
  19. /**
  20. * Aspose工具类
  21. */
  22. public class AsposeWordsUtils {
  23. // private static final Logger log = LoggerFactory.getLogger(AsposeWordsUtils.class);
  24. /**
  25. * 判断是否有授权文件 如果没有则会认为是试用版,转换的文件会有水印
  26. *
  27. * @return
  28. */
  29. public static boolean getLicense() throws Exception {
  30. boolean result = false;
  31. // 指定授权文件路径
  32. InputStream is = AsposeWordsUtils.class.getClassLoader().getResourceAsStream("license.xml");
  33. License aposeLic = new License();
  34. aposeLic.setLicense(is);
  35. result = true;
  36. return result;
  37. }
  38. /**
  39. * doc2pdf工具
  40. *
  41. * @param sourcerFile 源文件
  42. * @param targetFile 目标文件
  43. */
  44. public static void doc2pdf(String sourcerFile, String targetFile) throws Exception {
  45. if (!getLicense()) { // 验证License 若不验证则转化出的pdf文档会有水印产生
  46. return;
  47. }
  48. long old = System.currentTimeMillis();
  49. File file = new File(targetFile); // 新建一个空白pdf文档
  50. FileOutputStream os = new FileOutputStream(file);
  51. Document doc = new Document(sourcerFile); // sourcerFile是将要被转化的word文档
  52. doc.save(os, SaveFormat.PDF); // 全面支持DOC, DOCX, OOXML, RTF HTML, OpenDocument, PDF, EPUB, XPS, SWF 相互转换
  53. os.close();
  54. long now = System.currentTimeMillis();
  55. // log.info("共耗时:" + ((now - old) / 1000.0) + "秒"); //转化用时
  56. }
  57. /**
  58. * 获取Word模板文档填充工具
  59. *
  60. * 填充规则:
  61. * 模板(自定义):{{templateField}}
  62. * 数据集:key为templateField(不带括号),value为需要填充的任意数据
  63. *
  64. * @param inFilePath 文档路径
  65. * @param outFilePath 输出文档路径
  66. * @param data 待替换的域的键值
  67. * @param signCode 签章图片base64编码
  68. * @return 处理结果
  69. * @throws Exception
  70. */
  71. public static Map<String, String> readwriteWord(String inFilePath, String outFilePath, Map<Object, Object> data, String signCode) throws Exception {
  72. // 验证License 若不验证则转化出的word文档会有水印产生
  73. if (!getLicense()) {
  74. return null;
  75. }
  76. Map<String, String> map = new HashMap<>();
  77. Document doc = null;
  78. // 定义文档接口
  79. doc = new Document(inFilePath);
  80. for (Map.Entry<Object, Object> entry : data.entrySet()) {
  81. String key = entry.getKey().toString();
  82. Object value = entry.getValue();
  83. if (value != null) {
  84. Pattern p = Pattern.compile("\n");
  85. Matcher m = p.matcher(value.toString());
  86. int i = 0;
  87. String replaceValue = "";
  88. Boolean flag = false;
  89. while (m.find()) {
  90. // 换行处理
  91. replaceValue = value.toString().replace(m.group(), "" + ControlChar.LINE_BREAK + "");
  92. flag = true;
  93. }
  94. if (flag) {
  95. // 要求替换的内容是完全匹配时的替换
  96. // 可更换模板样式
  97. doc.getRange().replace("{{" + key + "}}", replaceValue, true, false);
  98. } else {
  99. // 要求替换的内容是完全匹配时的替换
  100. doc.getRange().replace("{{" + key + "}}", value.toString(), true, false);
  101. }
  102. }
  103. }
  104. if(signCode != null && !signCode.isEmpty()){
  105. DocumentBuilder builder = new DocumentBuilder(doc);
  106. builder.moveToMergeField("signature");
  107. BASE64Decoder decoder = new BASE64Decoder();
  108. byte[] imgByteArr = decoder.decodeBuffer(signCode);
  109. for (int i = 0; i < imgByteArr.length; ++i) {
  110. if (imgByteArr[i] < 0) {// 调整异常数据
  111. imgByteArr[i] += 256;
  112. }
  113. }
  114. builder.insertImage(imgByteArr, RelativeHorizontalPosition.RIGHT_MARGIN, -160, RelativeVerticalPosition.OUTSIDE_MARGIN, -140, 160, 160, WrapType.NONE);
  115. }
  116. // 生成四位随机数
  117. Random ne = new Random();
  118. int num = ne.nextInt(9999 - 1000 + 1) + 1000;
  119. String random = String.valueOf(num);
  120. String fileName = "" + System.currentTimeMillis() + random;
  121. //替换后的Word
  122. String docPath = outFilePath + fileName + inFilePath.substring(inFilePath.lastIndexOf("."));
  123. //生成的PDF位置
  124. String pdfPath = outFilePath + fileName + ".pdf";
  125. doc.save(docPath);
  126. File file = new File(docPath);
  127. if (file.exists()) {
  128. // 直接转换为word
  129. doc2pdf(docPath, pdfPath);
  130. map.put("pdfPath", pdfPath);
  131. map.put("docPath", docPath);
  132. }
  133. return map;
  134. }
  135. /**
  136. * Word中动态生成表格数据
  137. * @param inFilePath 源文件
  138. * @param outFilePath 目标文件
  139. * @param listOne 数据集1
  140. * @param listTwo 数据集2
  141. * @throws Exception
  142. */
  143. public static void dynamicDealTableData(String inFilePath, String outFilePath, List<DataOne> listOne, List<DataTwo> listTwo) throws Exception {
  144. // 验证License
  145. if (!getLicense()) {
  146. return;
  147. }
  148. DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
  149. // 读取源文件
  150. Document doc = new Document(inFilePath);
  151. // 获取word中的第一个表格,index指定表格位置
  152. Table table = (Table) doc.getChild(NodeType.TABLE, 0, true);
  153. /**
  154. * 下边的处理方式可能比较繁琐,总之逻辑就是:
  155. * 如果数据集1有数据的话,就要根据 数据集1 的 模板行号 加上 其数据量来计算出
  156. * 数据集2 的 模板行号 再处理
  157. */
  158. // 数据集1 模板数据首行
  159. int DataOneLineNum = 7;
  160. // 处理 数据集1
  161. if (CollectionUtils.isEmpty(listOne)) { // 数据集1 无数据
  162. // 获取模板行
  163. Range dataOneRange = table.getRows().get(DataOneLineNum).getRange();
  164. dataOneRange.replace("Index", String.valueOf(1), true, true);
  165. dataOneRange.replace("ApplyNoItem", "", true, true);
  166. dataOneRange.replace("ApplyNameItem", "", true, true);
  167. dataOneRange.replace("ApplyDateItem", "", true, true);
  168. // 数据集2 模板数据首行(无 数据集1数据)
  169. int listTwoLineNum_NonListOne = DataOneLineNum + 2;
  170. // 无 数据集2数据
  171. if (CollectionUtils.isEmpty(listTwo)) {
  172. // 获取模板行
  173. Range listTwoRange = table.getRows().get(listTwoLineNum_NonListOne).getRange();
  174. listTwoRange.replace("IndexMotel", String.valueOf(1), true, true);
  175. listTwoRange.replace("ApplyNo", "", true, true);
  176. listTwoRange.replace("ApplyDate", "", true, true);
  177. listTwoRange.replace("ApplyName", "", true, true);
  178. listTwoRange.replace("UploaderType", "", true, true);
  179. listTwoRange.replace("Remarks", "", true, true);
  180. } else {
  181. int temp = listTwoLineNum_NonListOne;
  182. for (int index = 0; index < listTwo.size(); index++) {
  183. // 复制模板行
  184. Node node = table.getRows().get(listTwoLineNum_NonListOne).deepClone(true);
  185. // 插入到第八行后
  186. table.getRows().insert(temp + 1, node);
  187. Range applyValidAmountRange = table.getRows().get(temp + 1).getRange();
  188. // 加行
  189. temp++;
  190. // 序号
  191. applyValidAmountRange.replace("IndexMotel", String.valueOf(index + 1), true, true);
  192. // 申请号
  193. if (StringUtils.isNotBlank(listTwo.get(index).getApplyNo())) {
  194. applyValidAmountRange.replace("ApplyNo", listTwo.get(index).getApplyNo(), true, true);
  195. }else{
  196. applyValidAmountRange.replace("ApplyNo", "", true, true);
  197. }
  198. // 申请日期
  199. if (Objects.nonNull(listTwo.get(index).getApplyDate())) {
  200. applyValidAmountRange.replace("ApplyDate", df.format(listTwo.get(index).getApplyDate()), true, true);
  201. }else{
  202. applyValidAmountRange.replace("ApplyDate", "", true, true);
  203. }
  204. // 名称
  205. if (StringUtils.isNotBlank(listTwo.get(index).getApplyName())) {
  206. applyValidAmountRange.replace("ApplyName", listTwo.get(index).getApplyName(), true, true);
  207. }else{
  208. applyValidAmountRange.replace("ApplyName", "", true, true);
  209. }
  210. // 类型
  211. if (StringUtils.isNotBlank(listTwo.get(index).getType())) {
  212. applyValidAmountRange.replace("UploaderType", listTwo.get(index).getType(), true, true);
  213. }else{
  214. applyValidAmountRange.replace("UploaderType", "", true, true);
  215. }
  216. // 备注
  217. if (StringUtils.isNotBlank(listTwo.get(index).getApplyRemark())) {
  218. applyValidAmountRange.replace("Remarks", listTwo.get(index).getApplyRemark(), true, true);
  219. }else{
  220. applyValidAmountRange.replace("Remarks", "", true, true);
  221. }
  222. }
  223. // 删除模板行数据
  224. table.getRows().get(listTwoLineNum_NonListOne).remove();
  225. }
  226. } else {
  227. // 处理 数据集1
  228. int temp = DataOneLineNum;
  229. for (int index = 0; index < listOne.size(); index++) {
  230. // 复制第八行
  231. Node node = table.getRows().get(DataOneLineNum).deepClone(true);
  232. // 插入到第八行后
  233. table.getRows().insert(temp + 1, node);
  234. Range range = table.getRows().get(temp + 1).getRange();
  235. // 加行
  236. temp++;
  237. // 序号
  238. range.replace("Index", String.valueOf(index + 1), true, true);
  239. // 申请号
  240. if (StringUtils.isNotBlank(listOne.get(index).getApplyNo())) {
  241. range.replace("ApplyNoItem", listOne.get(index).getApplyNo(), true, true);
  242. }else{
  243. range.replace("ApplyNoItem", "", true, true);
  244. }
  245. // 名称
  246. if (StringUtils.isNotBlank(listOne.get(index).getApplyName())) {
  247. range.replace("ApplyNameItem", listOne.get(index).getApplyName(), true, true);
  248. }else {
  249. range.replace("ApplyNameItem", "", true, true);
  250. }
  251. // 申请日期
  252. if (StringUtils.isNotBlank(df.format(listOne.get(index).getApplyDate()))) {
  253. range.replace("ApplyDateItem", df.format(listOne.get(index).getApplyDate()), true, true);
  254. } else {
  255. range.replace("ApplyDateItem", "", true, true);
  256. }
  257. }
  258. // 数据集2 数据首行(存在 数据集1)
  259. int dataTwoLineNum = DataOneLineNum + listTwo.size() + 2;
  260. // 处理 截至上一年度在审未决和有效授权专利拥有量
  261. if (CollectionUtils.isEmpty(listTwo)) {
  262. // 获取模板行
  263. Range dataTwoRange = table.getRows().get(dataTwoLineNum).getRange();
  264. dataTwoRange.replace("IndexMotel", String.valueOf(1), true, true);
  265. dataTwoRange.replace("ApplyNo", "", true, true);
  266. dataTwoRange.replace("ApplyDate", "", true, true);
  267. dataTwoRange.replace("ApplyName", "", true, true);
  268. dataTwoRange.replace("UploaderType", "", true, true);
  269. dataTwoRange.replace("Remarks", "", true, true);
  270. } else {
  271. int temp2 = dataTwoLineNum;
  272. for (int index = 0; index < listTwo.size(); index++) {
  273. // 复制模板行
  274. Node node = table.getRows().get(dataTwoLineNum).deepClone(true);
  275. // 插入到第八行后
  276. table.getRows().insert(temp2 + 1, node);
  277. Range applyValidAmountRange = table.getRows().get(temp2 + 1).getRange();
  278. // 加行
  279. temp2++;
  280. // 序号
  281. applyValidAmountRange.replace("IndexMotel", String.valueOf(index + 1), true, true);
  282. // 申请号
  283. if (StringUtils.isNotBlank(listTwo.get(index).getApplyNo())) {
  284. applyValidAmountRange.replace("ApplyNo", listTwo.get(index).getApplyNo(), true, true);
  285. }else{
  286. applyValidAmountRange.replace("ApplyNo", "", true, true);
  287. }
  288. // 申请日期
  289. if (Objects.nonNull(listTwo.get(index).getApplyDate())) {
  290. applyValidAmountRange.replace("ApplyDate", df.format(listTwo.get(index).getApplyDate()), true, true);
  291. }else{
  292. applyValidAmountRange.replace("ApplyDate", "", true, true);
  293. }
  294. // 名称
  295. if (StringUtils.isNotBlank(listTwo.get(index).getApplyName())) {
  296. applyValidAmountRange.replace("ApplyName", listTwo.get(index).getApplyName(), true, true);
  297. }else{
  298. applyValidAmountRange.replace("ApplyName", "", true, true);
  299. }
  300. // 类型
  301. if (StringUtils.isNotBlank(listTwo.get(index).getType())) {
  302. applyValidAmountRange.replace("UploaderType", listTwo.get(index).getType(), true, true);
  303. }else{
  304. applyValidAmountRange.replace("UploaderType", "", true, true);
  305. }
  306. // 备注
  307. if (StringUtils.isNotBlank(listTwo.get(index).getApplyRemark())) {
  308. applyValidAmountRange.replace("Remarks", listTwo.get(index).getApplyRemark(), true, true);
  309. }else{
  310. applyValidAmountRange.replace("Remarks", "", true, true);
  311. }
  312. }
  313. // 删除模板行数据
  314. table.getRows().get(DataOneLineNum + listTwo.size() + 2).remove();
  315. }
  316. // 删除 数据集1 模板行
  317. table.getRows().get(DataOneLineNum).remove();
  318. }
  319. // 保存到本地
  320. doc.save(outFilePath);
  321. }
  322. }

2. 测试用例

模板:template1.docx

  1. import java.io.File;
  2. import java.time.LocalDateTime;
  3. import java.util.ArrayList;
  4. import java.util.List;
  5. /**
  6. * @author maomaochong
  7. * @date 2021/11/29 10:49
  8. **/
  9. public class Test03 {
  10. public static void main(String[] args) throws Exception {
  11. test01();
  12. }
  13. private static void test01() throws Exception {
  14. List<DataOne> listOne = new ArrayList<>();
  15. List<DataTwo> listTwo = new ArrayList<>();
  16. listOne.add(new DataOne("123", "1123", LocalDateTime.now(), "ceshi", "123123"));
  17. listOne.add(new DataOne("333", "2231", LocalDateTime.now(), "ceshi2", "123113"));
  18. listTwo.add(new DataTwo("1122", "2233", "123133", LocalDateTime.now(), "测试1", "类型1", "备注"));
  19. listTwo.add(new DataTwo("3322", "1221", "123133", LocalDateTime.now(), "测试2", "类型2", "备注2"));
  20. AsposeWordsUtils.dynamicDealTableData(System.getProperty("user.dir") + File.separator + "template1.docx", System.getProperty("user.dir") + File.separator + "wordTemplate\\out\\dest3.docx", listOne, listTwo);
  21. }
  22. }

生成效果:
image.png