BokeWordUtils.java 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. package platform.common.util;
  2. import org.apache.poi.POIXMLDocument;
  3. import org.apache.poi.xwpf.usermodel.*;
  4. import javax.servlet.ServletOutputStream;
  5. import javax.servlet.http.HttpServletResponse;
  6. import java.io.File;
  7. import java.io.FileOutputStream;
  8. import java.io.IOException;
  9. import java.io.OutputStream;
  10. import java.net.URLEncoder;
  11. import java.util.*;
  12. /**
  13. * @author kevin
  14. * @since 2019/9/3 4:10 PM
  15. */
  16. public class BokeWordUtils {
  17. private XWPFDocument document;
  18. /**
  19. * 构造函数
  20. *
  21. * @param inputUrl
  22. * @param tableList
  23. */
  24. public BokeWordUtils(String inputUrl, List<String[]> tableList) {
  25. this(inputUrl, new HashMap<>(), tableList);
  26. }
  27. public BokeWordUtils(String inputUrl, Map<String, String> textMap, List<String[]> tableList) {
  28. try {
  29. System.out.println("===============开始读取word模版===================");
  30. System.out.println(Thread.currentThread().getName() + "\t 模版路径:" + inputUrl);
  31. document = new XWPFDocument(POIXMLDocument.openPackage(inputUrl));
  32. //解析替换文本段落对象
  33. changeText(document, textMap);
  34. //解析替换表格对象
  35. changeTable(document, textMap, tableList);
  36. System.out.println("生成完成!");
  37. } catch (IOException e) {
  38. e.printStackTrace();
  39. }
  40. }
  41. /**
  42. * 根据模板生成新word文档
  43. * 判断表格是需要替换还是需要插入,判断逻辑有$为替换,表格无$为插入
  44. *
  45. * @param inputUrl 模板存放地址
  46. * @param outputUrl 新文档存放地址
  47. * @param textMap 需要替换的信息集合
  48. * @param tableList 需要插入的表格信息集合
  49. */
  50. public static boolean changWord(String inputUrl, String outputUrl,
  51. Map<String, String> textMap, List<String[]> tableList) {
  52. //模板转换默认成功
  53. boolean changeFlag = true;
  54. try {
  55. //获取docx解析对象
  56. XWPFDocument document = new XWPFDocument(POIXMLDocument.openPackage(inputUrl));
  57. //解析替换文本段落对象
  58. BokeWordUtils.changeText(document, textMap);
  59. //解析替换表格对象
  60. BokeWordUtils.changeTable(document, textMap, tableList);
  61. //生成新的word
  62. File file = new File(outputUrl);
  63. FileOutputStream stream = new FileOutputStream(file);
  64. document.write(stream);
  65. stream.close();
  66. System.out.println("成功生成!");
  67. } catch (IOException e) {
  68. e.printStackTrace();
  69. changeFlag = false;
  70. }
  71. return changeFlag;
  72. }
  73. /**
  74. * 根据模板生成新word文档
  75. * 判断表格是需要替换还是需要插入,判断逻辑有$为替换,表格无$为插入
  76. * author:hzh
  77. * description:改为下载
  78. *
  79. * @param inputUrl 模板存放地址
  80. * @param response 新文档存放地址
  81. * @param textMap 需要替换的信息集合
  82. * @param tableList 需要插入的表格信息集合
  83. */
  84. public static boolean changWord(String inputUrl, HttpServletResponse response,
  85. Map<String, String> textMap, List<String[]> tableList) {
  86. //模板转换默认成功
  87. boolean changeFlag = true;
  88. try {
  89. //获取docx解析对象
  90. XWPFDocument document = new XWPFDocument(POIXMLDocument.openPackage(inputUrl));
  91. //解析替换文本段落对象
  92. BokeWordUtils.changeText(document, textMap);
  93. //解析替换表格对象
  94. BokeWordUtils.changeTable(document, textMap, tableList);
  95. //生成新的word
  96. OutputStream os = response.getOutputStream();
  97. document.write(os);
  98. os.close();
  99. System.out.println("成功生成!");
  100. } catch (IOException e) {
  101. e.printStackTrace();
  102. changeFlag = false;
  103. }
  104. return changeFlag;
  105. }
  106. /**
  107. * 输出到客户端
  108. *
  109. * @param fileName 输出文件名
  110. */
  111. public BokeWordUtils write(HttpServletResponse response, String fileName) throws IOException {
  112. response.reset();
  113. response.setContentType("application/octet-stream; charset=utf-8");
  114. response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
  115. write(response.getOutputStream());
  116. return this;
  117. }
  118. /**
  119. * 输出数据流
  120. *
  121. * @param os 输出数据流
  122. */
  123. public BokeWordUtils write(OutputStream os) throws IOException {
  124. document.write(os);
  125. return this;
  126. }
  127. /**
  128. * 替换段落文本
  129. *
  130. * @param document docx解析对象
  131. * @param textMap 需要替换的信息集合
  132. */
  133. public static void changeText(XWPFDocument document, Map<String, String> textMap) {
  134. //获取段落集合
  135. List<XWPFParagraph> paragraphs = document.getParagraphs();
  136. for (XWPFParagraph paragraph : paragraphs) {
  137. //判断此段落时候需要进行替换
  138. String text = paragraph.getText();
  139. if (checkText(text)) {
  140. List<XWPFRun> runs = paragraph.getRuns();
  141. for (XWPFRun run : runs) {
  142. //替换模板原来位置
  143. // run.setText(changeValue(run.toString(), textMap),0);
  144. String textValue = changeValue(run.toString(), textMap);
  145. run.setText(textValue, 0);
  146. }
  147. }
  148. }
  149. }
  150. /**
  151. * 替换表格对象方法
  152. *
  153. * @param document docx解析对象
  154. * @param textMap 需要替换的信息集合
  155. * @param tableList 需要插入的表格信息集合
  156. */
  157. public static void changeTable(XWPFDocument document, Map<String, String> textMap,
  158. List<String[]> tableList) {
  159. //获取表格对象集合
  160. List<XWPFTable> tables = document.getTables();
  161. for (int i = 0; i < tables.size(); i++) {
  162. //只处理行数大于等于2的表格,且不循环表头
  163. XWPFTable table = tables.get(i);
  164. //判断表格是需要替换还是需要插入,判断逻辑有$为替换,表格无$为插入
  165. if (checkText(table.getText())) {
  166. List<XWPFTableRow> rows = table.getRows();
  167. //遍历表格,并替换模板
  168. eachTable(rows, textMap);
  169. } else {
  170. insertTable(table, tableList);
  171. }
  172. }
  173. }
  174. /**
  175. * 遍历表格
  176. *
  177. * @param rows 表格行对象
  178. * @param textMap 需要替换的信息集合
  179. */
  180. public static void eachTable(List<XWPFTableRow> rows, Map<String, String> textMap) {
  181. for (XWPFTableRow row : rows) {
  182. List<XWPFTableCell> cells = row.getTableCells();
  183. for (XWPFTableCell cell : cells) {
  184. //判断单元格是否需要替换
  185. if (checkText(cell.getText())) {
  186. List<XWPFParagraph> paragraphs = cell.getParagraphs();
  187. for (XWPFParagraph paragraph : paragraphs) {
  188. List<XWPFRun> runs = paragraph.getRuns();
  189. for (XWPFRun run : runs) {
  190. run.setText(changeValue(run.toString(), textMap), 0);
  191. }
  192. }
  193. }
  194. }
  195. }
  196. }
  197. /**
  198. * 为表格插入数据,行数不够添加新行
  199. *
  200. * @param table 需要插入数据的表格
  201. * @param tableList 插入数据集合
  202. */
  203. public static void insertTable(XWPFTable table, List<String[]> tableList) {
  204. //创建行,根据需要插入的数据添加新行,不处理表头
  205. for (int i = 0; i < tableList.size(); i++) {
  206. XWPFTableRow row = table.createRow();
  207. List<XWPFTableCell> cells = row.getTableCells();
  208. for (int j = 0; j < cells.size(); j++) {
  209. XWPFTableCell cell = cells.get(j);
  210. cell.setText(tableList.get(i)[j]);
  211. }
  212. }
  213. //遍历表格插入数据
  214. // List<XWPFTableRow> rows = table.getRows();
  215. // for(int i = 1; i < rows.size(); i++){
  216. // XWPFTableRow newRow = table.getRow(i);
  217. // List<XWPFTableCell> cells = newRow.getTableCells();
  218. // for(int j = 0; j < cells.size(); j++){
  219. // XWPFTableCell cell = cells.get(j);
  220. // cell.setText(tableList.get(i-1)[j]);
  221. // }
  222. // }
  223. }
  224. /**
  225. * 判断文本中时候包含$
  226. *
  227. * @param text 文本
  228. * @return 包含返回true, 不包含返回false
  229. */
  230. public static boolean checkText(String text) {
  231. boolean check = false;
  232. if (text.indexOf("$") != -1) {
  233. check = true;
  234. }
  235. return check;
  236. }
  237. /**
  238. * 匹配传入信息集合与模板
  239. *
  240. * @param value 模板需要替换的区域
  241. * @param textMap 传入信息集合
  242. * @return 模板需要替换区域信息集合对应值
  243. */
  244. public static String changeValue(String value, Map<String, String> textMap) {
  245. Set<Map.Entry<String, String>> textSets = textMap.entrySet();
  246. for (Map.Entry<String, String> textSet : textSets) {
  247. //匹配模板与替换值 格式${key}
  248. String key = "${" + textSet.getKey() + "}";
  249. if (value.indexOf(key) != -1) {
  250. // value = textSet.getValue();//全部参数替换
  251. if (value != null && textSet != null && textSet.getValue() != null) {
  252. try {
  253. value = value.replace(key, textSet.getValue());//仅替换参数
  254. }catch (Exception e){
  255. System.out.println(e.getMessage());
  256. }
  257. }
  258. }
  259. }
  260. //模板未匹配到区域替换为空
  261. if (checkText(value)) {
  262. value = "";
  263. }
  264. return value;
  265. }
  266. public static void main(String[] args) {
  267. //模板文件地址
  268. String inputUrl = "/.docx";
  269. //新生产的模板文件
  270. String outputUrl = "/.docx";
  271. Map<String, String> testMap = new HashMap<String, String>();
  272. testMap.put("name", "小明");
  273. testMap.put("sex", "男");
  274. testMap.put("age", "18");
  275. testMap.put("address", "北京市");
  276. testMap.put("neirong", "好的内容");
  277. testMap.put("xuehao", "88888888");
  278. testMap.put("companyName", "软通");
  279. testMap.put("bigName", "项目大类");
  280. testMap.put("smileName", "项目大类-小类");
  281. testMap.put("phone", "18388888888");
  282. testMap.put("date", DateUtil.getTimeString(new Date()));
  283. List<String[]> tableList = new ArrayList<>();
  284. tableList.add(new String[]{"1", "1mm", "1开开", "1uu", "5", "6", "7", "8", "9"});
  285. tableList.add(new String[]{"2", "1mm", "1开开", "1uu", "5", "6", "7", "8", "9"});
  286. tableList.add(new String[]{"3", "1mm", "1开开", "1uu", "5", "6", "7", "8", "9"});
  287. tableList.add(new String[]{"4", "1mm", "1开开", "1uu", "5", "6", "7", "8", "9"});
  288. tableList.add(new String[]{"5", "1mm", "1开开", "1uu", "5", "6", "7", "8", "9"});
  289. tableList.add(new String[]{"6", "1mm", "1开开", "1uu", "5", "6", "7", "8", "9"});
  290. tableList.add(new String[]{"7", "1mm", "1开开", "1uu", "5", "6", "7", "8", "9"});
  291. // tableList.add(new String[]{"2","2密码","2B","基金C"});
  292. // tableList.add(new String[]{"3","3看看","3B","基看C"});
  293. // tableList.add(new String[]{"4","4累了","4B","4谁说的"});
  294. BokeWordUtils.changWord(inputUrl, outputUrl, testMap, tableList);
  295. }
  296. }