一,网上的API讲解

其实POI的生成Word文档的规则就是先把获取到的数据转成xml格式的数据,然后通过xpath解析表单式的应用取值,判断等等,然后在把取到的值放到word文档中,最后在输出来。

1.1,参考一

1、poi之word文档结构介绍之正文段落

一个文档包含多个段落,一个段落包含多个Runs,一个Runs包含多个Run,Run是文档的最小单元

获取所有段落:List<XWPFParagraph> paragraphs = word.getParagraphs();

获取一个段落中的所有Runs:List<XWPFRun> xwpfRuns = xwpfParagraph.getRuns();

获取一个Runs中的一个Run:XWPFRun run = xwpfRuns.get(index);

2、poi之word文档结构介绍之正文表格

一个文档包含多个表格,一个表格包含多行,一行包含多列(格),每一格的内容相当于一个完整的文档

获取所有表格:List<XWPFTable> xwpfTables = doc.getTables();

获取一个表格中的所有行:List<XWPFTableRow> xwpfTableRows = xwpfTable.getRows();

获取一行中的所有列:List<XWPFTableCell> xwpfTableCells = xwpfTableRow.getTableCells();

获取一格里的内容:List<XWPFParagraph> paragraphs = xwpfTableCell.getParagraphs();

之后和正文段落一样

注:

  1. 表格的一格相当于一个完整的docx文档,只是没有页眉和页脚。里面可以有表格,使用xwpfTableCell.getTables()获取,and so on
  2. 在poi文档中段落和表格是完全分开的,如果在两个段落中有一个表格,在poi中是没办法确定表格在段落中间的。(当然除非你本来知道了,这句是废话)。只有文档的格式固定,才能正确的得到文档的结构

3、poi之word文档结构介绍之页眉:

一个文档可以有多个页眉(不知道怎么会有多个页眉。。。),页眉里面可以包含段落和表格

获取文档的页眉:List<XWPFHeader> headerList = doc.getHeaderList();

获取页眉里的所有段落:List<XWPFParagraph> paras = header.getParagraphs();

获取页眉里的所有表格:List<XWPFTable> tables = header.getTables();

之后就一样了

4、poi之word文档结构介绍之页脚:

页脚和页眉基本类似,可以获取表示页数的角标

1.2,参考二

POI操作Word简介

POI读写Excel功能强大、操作简单。但是POI操作时,一般只用它读取word文档,POI只能能够创建简单的word文档,相对而言POI操作时的功能太少。

(2)POI创建Word文档的简单示例

  1. XWPFDocument doc = new XWPFDocument();// 创建Word文件
  2. XWPFParagraph p = doc.createParagraph();// 新建一个段落
  3. p.setAlignment(ParagraphAlignment.CENTER);// 设置段落的对齐方式
  4. p.setBorderBottom(Borders.DOUBLE);//设置下边框
  5. p.setBorderTop(Borders.DOUBLE);//设置上边框
  6. p.setBorderRight(Borders.DOUBLE);//设置右边框
  7. p.setBorderLeft(Borders.DOUBLE);//设置左边框
  8. XWPFRun r = p.createRun();//创建段落文本
  9. r.setText("POI创建的Word段落文本");
  10. r.setBold(true);//设置为粗体
  11. r.setColor("FF0000");//设置颜色
  12. p = doc.createParagraph();// 新建一个段落
  13. r = p.createRun();
  14. r.setText("POI读写Excel功能强大、操作简单。");
  15. XWPFTable table= doc.createTable(3, 3);//创建一个表格
  16. table.getRow(0).getCell(0).setText("表格1");
  17. table.getRow(1).getCell(1).setText("表格2");
  18. table.getRow(2).getCell(2).setText("表格3");
  19. FileOutputStream out = newFileOutputStream("d:\\POI\\sample.doc");
  20. doc.write(out);
  21. out.close();

(3)POI读取Word文档里的文字

  1. FileInputStream stream = newFileInputStream("d:\\POI\\sample.doc");
  2. XWPFDocument doc = new XWPFDocument(stream);// 创建Word文件
  3. for(XWPFParagraph p : doc.getParagraphs())//遍历段落
  4. {
  5.  System.out.print(p.getParagraphText());
  6. }
  7. for(XWPFTable table : doc.getTables())//遍历表格
  8. {
  9.  for(XWPFTableRow row : table.getRows())
  10.  {
  11.  for(XWPFTableCell cell : row.getTableCells())
  12.  {
  13.  System.out.print(cell.getText());
  14.  }
  15.  }

1.3,参考三,分段混乱

题:在操作POI替换world时发现getRuns将我们预设的${product}自动切换成了

  ${product, }]
${product  }
成了两个部分
  • 1
  • 2
  • 3

 
解决方法一。(未尝试) 
强制把List中的内容合并成一个字符串,替换内容后,把段落中的XWPFRun全部remove掉,然后新建一个含有替换后内容的XPWFRun,并赋给当前段落。 
解决方法二. 
请用复制粘贴把你的${product}添加进world文档里面即可解决,不要手打 目前发现复制粘贴是没有问题的,感觉像是poi的一个bug不知道立贴为证。

注意:${这里尽量不要存中文,否在还出现上面情况}

二,项目应用

2.1,判断生成word的条件

 1     private boolean getXpathRes(String json,String xpathRule){2         boolean isTrue = false;3         try {4             JSONObject obj = getGoodJson(json, json.replaceAll("\n", "").replaceAll("\"null\"", "\"\"").replaceAll(":null,", ":\"\",").replaceAll(" \"", "\""));5             XMLSerializer serializer = new XMLSerializer();6             String xml = serializer.write(obj,"UTF-8");7             log.info("测试用的,记得删除"+xml);8             DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();9             dbf.setValidating(false);
10             DocumentBuilder db;
11             db = dbf.newDocumentBuilder();
12             StringReader stringReader = new StringReader(xml);
13             InputSource inputSource = new InputSource(stringReader);
14             Document doc;
15             doc = db.parse(inputSource);
16             XPathFactory factory = XPathFactory.newInstance();
17             XPath  xpath = factory.newXPath();
18             isTrue = (Boolean) xpath.evaluate(xpathRule, doc,XPathConstants.BOOLEAN);
19         } catch (Exception e) {
20             log.info("合同解析生成XML报错:"+e.getMessage());
21         }finally{
22             return isTrue;
23         }
24 //        return true;
25     }

2.1.1,下面就是根据从数据库中取到值,判断规则,和json数据做对比的,就是json数据中有没有数据库中要的值。判断规则是xpath的规则运算符。

JSONObject obj = getGoodJson(json, json.replaceAll("\n", "").replaceAll("\"null\"", "\"\"").replaceAll(":null,", ":\"\",").replaceAll(" \"", "\""));
XMLSerializer serializer = new XMLSerializer();
String xml = serializer.write(obj,"UTF-8");

--把json格式的数据以xml的格式输出

首先得到:得到 DOM 解析器的工厂实例 
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

然后从 DOM 工厂获得 DOM 解析器

dbf.setValidating(false);默认是false
DocumentBuilder db;
db = dbf.newDocumentBuilder();

当你有一组应用程序接口(API)只允许用Writer或Reader作为输入,但你又想使用String,这时可以用StringWriter或StringReader。
当读入文件时也一样。可以用StringReader代替Reader来哄骗API,而不必非得从某种形式的文件中读入。StringReader的构造器要求一个String参数。例如:xmlReader.parse(new InputSource(new StringReader(xmlStr)));
StringReader stringReader = new StringReader(xml);

--- 把符合xml的String转成document对象被java程序解读
StringReader stringReader = new StringReader(xml);
InputSource inputSource = new InputSource(stringReader);
Document doc;
doc = db.parse(inputSource);

--用xpath解析
--生成xpath对象
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();

在 Java 中计算 XPath 表达式时,第二个参数指定需要的返回类型。有五种可能,都在javax.xml.xpath.XPathConstants 类中命名了常量:

XPathConstants.NODESET
XPathConstants.BOOLEAN
XPathConstants.NUMBER
XPathConstants.STRING
XPathConstants.NODE 获取节点 node.getTextContent() 获得节点的内容
xpathRule:数据库中存储的
//industrySubType!='20' and //industrySubType!='21' and //industrySubType!='22' and //industrySubType!='23' and //industrySubType!='26' and //industrySubType!='27' and //industrySubType!='28' and //industrySubType!='29' and //industrySubType!='30' and //industrySubType!='148' and //industrySubType!='31' and //industrySubType!='32' and //industrySubType!='37' and //industrySubType!='38' and //industrySubType!='39' and //industrySubType!='11' and //industrySubType!='12' and //industrySubType!='13' and //industrySubType!='14' and //industrySubType!='15' and //industrySubType!='16'

//标示节点中的所有的xml节点
doc就是经过一系列处理,把json数据转化成document对象,并且能被xpath解读的对象:

XPathConstants.BOOLEAN:是返回值,有这个数据就返回true,没有就是false
isTrue = (Boolean) xpath.evaluate(xpathRule, doc,XPathConstants.BOOLEAN);

这里需要见xpath的解析规则

xml的xPath解析规则

2.2,获取模板之后,开始获取里面的参数,这个参数是在数据库中配置的。

  1 private List<Map<String,Object>> getTemplateParam(List<String> templatNameList,2         String workOrderId, String productCode,String contractNum,long merchantCode,boolean isMainFlag) throws Exception 3     {4         log.info("======获取模板原始参数开始===获得的模板有templatNameList=="+templatNameList.toString()+"======");5         List<EcontractTemplateParamsqlAdvanced> aTemplateParamSqlList = new ArrayList<EcontractTemplateParamsqlAdvanced>();6         List<Map<String,Object>> aTemplateParamList = new ArrayList<Map<String,Object>>();7         8         List<EcontractTemplateParamAdvanced> orginParamList = new ArrayList<EcontractTemplateParamAdvanced>();9 10         Map<String, Object> rsMap = null;11         Map<String,Object> queryTemplateParamSql = new HashMap<String,Object>();12         Map<String,Object> queryOriginParam = new HashMap<String,Object>();13 14         //获取每个模板的参数 15         for(String templateName : templatNameList)16         {17             log.info("======获取模板原始参数中===获得的模板有templatNameList=="+templatNameList.toString()+"==成功获取到模板文档====");18             queryOriginParam.put("templateName", templateName.split(",")[0]);19             //aTemplateParamSqlList = econtractTemplateParamsqlAdvancedService.queryEcontractTemplateParamsqlAdvancedByMap(queryTemplateParamSql);20             //获取每个模板需要替换的参数21             orginParamList = econtractTemplateParamAdvancedService.queryEcontractTemplateParamAdvancedByMap(queryOriginParam);22             23             Map<String,String> searchMap = new HashMap<String,String>();24             searchMap.put("orderId", templateName.split(",")[1]);25             WorkorderDetail workOrderDeatil = workorderDetailService.findWorkorderDetailByMap(searchMap);26             String json = workOrderDeatil.getProductParam();27             String jsonStr = json.replaceAll("\n", "").replaceAll("\"null\"", "\"\"").replaceAll(":null,", ":\"\",").replaceAll(" \"", "\"");28             rsMap =  new HashMap<String,Object>();29             Map<String, Object> res = getGoodJsonMap(json, jsonStr);30             String xpath="";31             for(EcontractTemplateParamAdvanced paramAdvancde:orginParamList){32                 xpath = paramAdvancde.getMethodParam();33                 if(res.get(paramAdvancde.getFieldName())==null){34                     //新添加一个实现方式,是先判断是否满足前提条件,如果满足再查询数据,不满足就直接返回/35                     boolean judgeSuccess = false;//判断前提条件是否成立,false-不成立,true-成立36                     if(!StringUtils.isEmpty(xpath)&&xpath.indexOf("@&@")!=-1 || !StringUtils.isEmpty(xpath)&&xpath.indexOf("@&&@")!=-1){//存在这个符号,表示需要判断前提条件,@&@,前提条件,需要取的值的字段,单位37 //                        String[] methodParamArray = xpath.split(",");38                         String[] methodParamArray = null;39                         if(!StringUtils.isEmpty(xpath)&&xpath.indexOf("@&&@")!=-1){//@&&@,前提条件中存在特殊符号40                             methodParamArray = xpath.split(";");41                         }else{42                             methodParamArray = xpath.split(",");43                         }44                         if(getXpathRes(json,methodParamArray[1])){45                             judgeSuccess = true;//需要判断前提条件46                         }else{47                             String unit = methodParamArray[methodParamArray.length-1];48                             if("null".equals(unit)){//没有单位,在最后是单位49                                 rsMap.put(paramAdvancde.getFieldName(),"/");50                             }else if("instalmentsUnitToShow".equals(unit)){//分期付款特殊处理51                                 rsMap.put(paramAdvancde.getFieldName(),getInstalmentsUnit(methodParamArray));52                             }else if("hasNotUnit".equals(unit)){//针对单笔最高和单笔最低53                                 rsMap.put(paramAdvancde.getFieldName(),"");54                             }else{//有单位55                                 rsMap.put(paramAdvancde.getFieldName(),"/"+unit);56                             }57                         }58                     }else{//不需要判断前提条件59                         judgeSuccess = true;60                     }61                     // 此xpath表达式是用来做逻辑判断的62                     if(!StringUtils.isEmpty(xpath) && xpath.indexOf("=")!=-1 && judgeSuccess && xpath.indexOf("@&@")==-1 && xpath.indexOf("@&&@")==-1){//有=号并且不存在@&@,因为前提条件中会有=号63                         //快易花商户合同里不再是黑白框  应该是√和X   订单中各期商户补贴=0或空值时,为X,费率为/;  非空时,为√,费率取对应的值64                         if(xpath.indexOf("xx=xx")!=-1){//快易花中需要用替换的65                             if(getXpathRes(json,xpath)){66                                 rsMap.put(paramAdvancde.getFieldName(),"√");67                             }else{68                                 rsMap.put(paramAdvancde.getFieldName(),"×");69                             }70                         }else{//其他合同还是打■或□,快易花合同中部分也需要用■或□71                             if(getXpathRes(json,xpath)){72                                 rsMap.put(paramAdvancde.getFieldName(),"■");73                             }else{74                                 rsMap.put(paramAdvancde.getFieldName(),"□");75                             }76                         }77                     }else if(!StringUtils.isEmpty(xpath)&& (xpath.indexOf("=")==-1 || xpath.indexOf("@&@")!=-1 || xpath.indexOf("@&&@")!=-1)&&judgeSuccess){//没有=号或者有@&@符号的规则也需要走以下逻辑78                         //如果call_method为空,则直接利用xpath进行取值替换79                         String str =getXpathValue(json,paramAdvancde.getMethodParam());80                         if(StringUtils.isEmpty(paramAdvancde.getCallMethod())){81                             rsMap.put(paramAdvancde.getFieldName(),str);82                         }else{83                             // 用来判断机具类型的,固定POS、移动POS、快刷、POS附件84 //                            JSONObject obj = (JSONObject) JSONSerializer.toJSON(jsonStr);85                             JSONObject obj = getGoodJson(json, jsonStr);86                             JSONArray productListJsonArray = obj.getJSONArray("productList");87                             JSONArray PriSinBillCustomDataStr=null;88                             JSONArray PubSinBankCustomDataStr=null;89                             JSONArray PriBatchBankCustomDataStr=null;90                             JSONArray PubBatchBankCustomDataStr=null;91                             if("dspayfinancialupdatewordfir.docx".equals(templateName.split(",")[0])&&productListJsonArray.size()>0){92                                     JSONObject job = productListJsonArray.getJSONObject(0);  // 遍历 jsonarray 数组,把每一个对象转成 json 对象93                                     if(job.containsKey("PriSinBillCustomDataStr")&&job.getString("PriSinBillCustomDataStr")!=null&&xpath.indexOf("PriSinBillCustomDataStr")>-1){94                                          String PriBill =job.get("PriSinBillCustomDataStr").toString()==null?"":String.valueOf(job.get("PriSinBillCustomDataStr"));95                                          PriSinBillCustomDataStr = JSONArray.fromObject(PriBill);96                                     }else if(job.containsKey("PubSinBankCustomDataStr")&&job.getString("PubSinBankCustomDataStr")!=null&&xpath.indexOf("PubSinBankCustomDataStr")>-1){97                                          String PubBank =job.get("PubSinBankCustomDataStr").toString()==null?"":String.valueOf(job.get("PubSinBankCustomDataStr"));98                                          PubSinBankCustomDataStr = JSONArray.fromObject(PubBank);99                                     }else if(job.containsKey("PriBatchBankCustomDataStr")&&job.getString("PriBatchBankCustomDataStr")!=null&&xpath.indexOf("PriBatchBankCustomDataStr")>-1){
100                                          String PriBatch =job.get("PriBatchBankCustomDataStr").toString()==null?"":String.valueOf(job.get("PriBatchBankCustomDataStr"));
101                                          PriBatchBankCustomDataStr = JSONArray.fromObject(PriBatch);
102                                     }else if(job.containsKey("PubBatchBankCustomDataStr")&&job.getString("PubBatchBankCustomDataStr")!=null&&xpath.indexOf("PubBatchBankCustomDataStr")>-1){
103                                          String PubBatch =job.get("PubBatchBankCustomDataStr").toString()==null?"":String.valueOf(job.get("PubBatchBankCustomDataStr"));
104                                          PubBatchBankCustomDataStr = JSONArray.fromObject(PubBatch);
105                                     }
106                                 }
107                             JSONArray terminalArray =new JSONArray();
108                             JSONObject merchantFinanceObject = new JSONObject();//只有主协议需要这个
109                             String checkTemplateNameValue = checkTemplateName(templateName);
110                             // 如果产品涉及到终端直接去terminobjects中取值
111                             if("1".equals(checkTemplateNameValue)){//去terminobjects中取值
112                                 JSONObject terminalObject = (JSONObject)productListJsonArray.get(0);
113                                 if((JSONArray)terminalObject.get("terminalObjects")!= null){//防止空指针
114                                     terminalArray =(JSONArray)terminalObject.get("terminalObjects");
115                                 }
116                             /*}
117                             // 非终端相关的直接去productList中取值
118                             else{
119                                 terminalArray=productListJsonArray;*/
120                             }else if("2".equals(checkTemplateNameValue)){//去merchantFinance取值
121                                 if(obj.containsKey("merchantFinance")){
122                                     merchantFinanceObject = obj.getJSONObject("merchantFinance");//主协议的时候需要用到
123                                 }
124                             }
125                             if(productListJsonArray==null){
126                                 rsMap.put(paramAdvancde.getFieldName(),"");
127                             }else{
128                                 String methodParam =paramAdvancde.getMethodParam();
129 //                                String[] methodParamArray = methodParam.split(",");
130                                 String[] methodParamArray = null;
131                                 if(!StringUtils.isEmpty(xpath)&&xpath.indexOf("@&&@")!=-1){//@&&@,前提条件中存在特殊符号
132                                     methodParamArray = methodParam.split(";");
133                                 }else{
134                                     methodParamArray = methodParam.split(",");//tina623
135                                 }
136                                 Object[] args = null;
137                                 if("1".equals(checkTemplateNameValue)){//如果产品涉及到终端直接去terminobjects中取值
138                                     if(paramAdvancde.getCallMethod().startsWith("get")){
139                                         args = new Object[methodParamArray.length+2];
140                                         args[0]=productListJsonArray;
141                                         args[1]=terminalArray;//主协议的时候,merchantFinance取的arraylist封装在这里
142                                         for(int i=2;i<methodParamArray.length+2;i++){
143                                             args[i]=methodParamArray[i-2];
144                                         }
145                                     }else if(paramAdvancde.getCallMethod().startsWith("set")){
146                                         args = new Object[methodParamArray.length+1];
147                                         args[0]=templateName.split(",")[1];
148                                         for(int i=1;i<methodParamArray.length+1;i++){
149                                             args[i]=methodParamArray[i-1];
150                                         }
151                                     }
152                                 }else if("2".equals(checkTemplateNameValue)){//主协议---主协议的时候,merchantFinance取的arraylist封装在这里
153                                     if(paramAdvancde.getCallMethod().startsWith("get")){
154                                         args = new Object[methodParamArray.length+2];
155                                         args[0]=productListJsonArray;
156                                         args[1]=merchantFinanceObject;//主协议的时候,merchantFinance取的arraylist封装在这里
157                                         for(int i=2;i<methodParamArray.length+2;i++){
158                                             args[i]=methodParamArray[i-2];
159                                         }
160                                     }else if(paramAdvancde.getCallMethod().startsWith("set")){
161                                         args = new Object[methodParamArray.length+1];
162                                         args[0]=templateName.split(",")[1];
163                                         for(int i=1;i<methodParamArray.length+1;i++){
164                                             args[i]=methodParamArray[i-1];
165                                         }
166                                     }
167                                 }else{
168                                     args = new Object[methodParamArray.length+1];
169                                     // 约定取值方法以get起始的话
170                                     if(paramAdvancde.getCallMethod().startsWith("get")){
171                                         args[0]=productListJsonArray;
172                                     }else if(paramAdvancde.getCallMethod().startsWith("set")){
173                                         args[0]=templateName.split(",")[1];
174                                     }else if("dspayfinancialupdatewordfir.docx".equals(templateName.split(",")[0])&&paramAdvancde.getCallMethod().startsWith("twoGet")){
175                                         if(xpath.indexOf("PriSinBillCustomDataStr")>-1){
176                                             args[0]=PriSinBillCustomDataStr;
177                                         }else if(xpath.indexOf("PubSinBankCustomDataStr")>-1){
178                                             args[0]=PubSinBankCustomDataStr;
179                                         }else if(xpath.indexOf("PriBatchBankCustomDataStr")>-1){
180                                             args[0]=PriBatchBankCustomDataStr;
181                                         }else if(xpath.indexOf("PubBatchBankCustomDataStr")>-1){
182                                             args[0]=PubBatchBankCustomDataStr;
183                                         }
184                                     }
185                                     for(int i=1;i<methodParamArray.length+1;i++){
186                                         args[i]=methodParamArray[i-1];
187                                     }
188                                 }
189                                 Class[] argsClass = new Class[args.length];
190                                  for (int i = 0, j = args.length; i < j; i++) {
191                                      if(args[i]!=null){//避免空指针
192                                          argsClass[i] = args[i].getClass();
193                                      }
194                                  }
195                                 try{
196                                     Method method = crmContractUtil.getClass().getMethod(paramAdvancde.getCallMethod(),argsClass);
197                                     rsMap.put(paramAdvancde.getFieldName(),method.invoke(crmContractUtil, args));
198                                 }catch (Exception e) {
199                                     e.printStackTrace();
200                                 }
201                             }
202                         }
203                     }
204                 }
205                 else{
206                     if(res.get(paramAdvancde.getFieldName())!=null && !StringUtils.isEmpty(res.get(paramAdvancde.getFieldName()).toString())){
207                         rsMap.put(paramAdvancde.getFieldName(), res.get(paramAdvancde.getFieldName()));
208                     }else{
209                         rsMap.put(paramAdvancde.getFieldName(), "/");//如果获取的值为空,自动赋值/
210                     }
211                 }
212             }
213             rsMap.put("templateName", templateName.split(",")[0]);
214             rsMap = converMap(rsMap,contractNum,merchantCode,isMainFlag,res);//封装一些需要计算的数据
215             aTemplateParamList.add(rsMap);
216         }
217         log.info("获取模板原始参数成功..aTemplateParamList===="+aTemplateParamList.toString()+"===========");
218         return aTemplateParamList;
219     }
220     

 1 @SuppressWarnings("finally")2     private String getXpathValue(String json,String xpathRule){3         String xpathValue = "/";4         try {5             JSONObject obj = getGoodJson(json, json.replaceAll("\n", "").replaceAll("\"null\"", "\"\"").replaceAll(":null,", ":\"\",").replaceAll(" \"", "\""));6             XMLSerializer serializer = new XMLSerializer();7             String xml = serializer.write(obj,"UTF-8");  8             DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();9             dbf.setValidating(false);
10             DocumentBuilder db;
11             db = dbf.newDocumentBuilder();
12             StringReader stringReader = new StringReader(xml);
13             InputSource inputSource = new InputSource(stringReader);
14             Document doc;
15             doc = db.parse(inputSource);
16             XPathFactory factory = XPathFactory.newInstance();
17             XPath  xpath = factory.newXPath();
18             Node node     = (Node)xpath.evaluate(xpathRule, doc,XPathConstants.NODE);
19             if(node==null){
20                 return "/";
21             }
22             else{
23                 if(StringUtils.isEmpty(node.getTextContent())){
24                     return "/";
25                 }else{
26                     xpathValue = node.getTextContent();
27                     return node.getTextContent();
28                 }
29             }
30         } catch (Exception e) {
31             log.info("合同解析生成XML报错,xpathRule==="+xpathRule);
32             return "/";
33         }finally{
34             return xpathValue;
35         }
36 //        return "/";
37     }

划红线的是和一开始的是不一样的,这里是获取json转化过来的xml的文档的node节点的值的。

isTrue = (Boolean) xpath.evaluate(xpathRule, doc,XPathConstants.BOOLEAN);
是通过xpath判断
xpathRule有没有在经过封装的xml文件的document里面的。

orginParamList = econtractTemplateParamAdvancedService.queryEcontractTemplateParamAdvancedByMap(queryOriginParam);
--根据模板名称获取

res里面装的是json转化成的map数据,根据数据库查出来的key值,去取map中的value值。并全部放在map中来。
根据数据库中参数的设置来看取值的逻辑

取值逻辑1:

callmethod 空 methodparam 空
则什么都不往map中放

取值逻辑2:

callmethod 空 methodparam 有值:比如//merchantName
它的取值逻辑主要也是用到了上面的xpath的取值逻辑。
则去json转化的map中查找,找到数值后则放进map中来

取值逻辑3:

callmethod 空 methodparam 有值:比如//isApiPayToBank=1 or //isApiPayToBill=1 or //isBatchApiPayToBank=1
其实它和2的取值逻辑是一样的,只不过xpath的表单式不一样而已。

取值逻辑4:

callmethod 有值,方法名比如setMerchantProperty methodparam 有值:比如参数address或者IpAddress_sin_99bill/IpAddress_sin_ban/IpAddress_bat_ban 可以放值多个参数
这个一般是在json格式的数据中没有这个值,但是还要获取这个数据,通过反射找到setMerchantProperty 这个方法从数据库中其它表中来获取。
address它是参数,需要根据它往反射类中的反射方法中传递的参数。
而存在map中的key值则是word文档中的命名规则,比如input。value值则是从数据库中获取的。
则会通过反射的方法来获取数值。
可以看出来只要是input开头都是通过这个方法获取的。这个主要是为了给替换做准备的。

反射的逻辑详见

反射的一个案例分析

取值逻辑5:

假如上面的4套规则还不能解决一些问题的话,则通过代码直接来设置map的key和value值,放到map中来,比如说当前的时间等等。

rsMap.put(paramAdvancde.getFieldName(), res.get(paramAdvancde.getFieldName()));
最后通过得到的数据就是

1 [{payBankNum=/, cwmerchantContactEmail=222@qq.com, masterDay=11, masterContractNum=K18-2000-801-03, depositAccount=/, masterYear=2018, cwmerchantContactCz=/, masterMonth=2, billAccountShareAmount=/, merchantName=还有几天就要放假了呀, year=2018, telephoneormobilephone=18068334025, legalName=222, input28=名字, ddmerchantContactTelphone=18068334025, ddmerchantContactEmail=222@qq.com, bankName=/, cwmerchantContactName=名字, cwmerchantContactTelphone=18068334025, input3=广西壮族自治区贵港市, input5=3333, ddmerchantContactName=名字, input6=3333, input20=2018年 02月 11日, month=2, ddmerchantContactCz=/, merechantURL=/, input23=2019年 02月 10日, day=11, contract_num=K18-2000-801-03, templateName=dsmainfinancialword.docx, accessType=首选, bankAccount=/}, {perMinimum=, perMinimum_pri_bat_ban=, payToCompayKqAcccountRatio=/, payBankNum=/, input69=3333, perMinimum_pub_bat_99bill=, input19=/, payToCompayAcccount=□, input18=□, autoDiectPayOpenFee=/, perMaximum_pub_bat_99bill=/, masterDay=11, input13=□, masterYear=2018, masterMonth=2, holidayPayOpenFee=/, year=2018, holidayPayTechnologyFee=/, memberCode=10012401604, telephoneormobilephone=18068334025, accumulateQuota=/, groupPayCompayKqAccountRatio=/, perMinimum_pri_sin_ban=, perMaximum=/, apiZdPayOpenFee=/, KqPayXinShi=■, bankName=/, perMinimum_pri_bat_99bill=, cwmerchantContactTelphone=18068334025, perMaximum_pri_bat_99bill=/, ddmerchantContactName=名字, BankName_bat_ban_hidden=/, AccountNum_bat_ban=/, month=2, day=11, idContent=234324fds432@qq.com, contract_num=K18-2000-801-03, apiZdPayTechnologyFee=/, perMaximum_pub_bat_ban=/, cwmerchantContactEmail=222@qq.com, holidayPay=□, perMaximum_pri_sin_99bill=/, perMinimum_pub_sin_99bill=, masterContractNum=K18-2000-801-03, groupPayCompayBankAccountRatio=/, depositAccount=/, groupPayToPersonKqAccountRatio=/, cwmerchantContactCz=/, groupPayToPersonBankAccountRatio=/, merchantName=还有几天就要放假了呀, AccountName_bat_ban=/, isBatchApiPayToBank=□, perMaximum_pri_bat_ban=/, perMinimum_pri_sin_99bill=, perMaximum_pub_sin_ban=/, input28=□, ddmerchantContactEmail=222@qq.com, ddmerchantContactTelphone=18068334025, perMinimum_pub_sin_ban=, payToCompayBankAcccountRatio=/, payPersonBankAccountRatio=/, KqPayXinShiFeeRatio=0, payPersonAccountRatio=/, cwmerchantContactName=名字, input3=□, input7=□, input6=/, input9=□, perMaximum_pub_sin_99bill=/, autoDiectPay=□, input22=□, ddmerchantContactCz=/, input24=□, input26=□, perMinimum_pub_bat_ban=, bankAccount=/, templateName=dspayfinancialaddwordfir.docx, perMaximum_pri_sin_ban=/}, {telephoneormobilephone=18068334025, ddmerchantContactEmail=222@qq.com, ddmerchantContactTelphone=18068334025, bankName=/, payBankNum=/, cwmerchantContactEmail=222@qq.com, cwmerchantContactName=名字, cwmerchantContactTelphone=18068334025, masterDay=11, masterContractNum=K18-2000-801-03, ddmerchantContactName=名字, depositAccount=/, masterYear=2018, cwmerchantContactCz=/, masterMonth=2, month=2, merchantName=还有几天就要放假了呀, year=2018, ddmerchantContactCz=/, day=11, contract_num=K18-2000-801-03, templateName=quickSubjectSeal.docx, bankAccount=/}]

至于上面的KqPayXinShi=■
则是按照上面的判断规则直接获取就行了

if(getXpathRes(json,xpath)){
rsMap.put(paramAdvancde.getFieldName(),"■");
}else{
rsMap.put(paramAdvancde.getFieldName(),"□");
}

2.3,替换模板参数

 1     private List<Map<String,Object>> replaceParam(List<Map<String,Object>> templatParamList,String contractNum,long contractType)2     {3         List<Map<String,Object>> originParamList = new ArrayList<Map<String,Object>>();4         if(!templatParamList.isEmpty())5         {6             String templateName = "";7             Map<String,Object> resultMap = null;8             Map<String,Object> queryOriginParam = new HashMap<String,Object>();9             List<EcontractTemplateParamAdvanced> aTemplateParamList = new ArrayList<EcontractTemplateParamAdvanced>();
10             for(Map<String,Object> originParam : templatParamList)
11             {
12                 templateName = (String) originParam.get("templateName");
13                 queryOriginParam.put("templateName", templateName);
14
15                 //获取每个模板需要替换的参数
16                 aTemplateParamList = econtractTemplateParamAdvancedService.queryEcontractTemplateParamAdvancedByMap(queryOriginParam);
17                 if(!aTemplateParamList.isEmpty())
18                 {
19                     resultMap = new HashMap<String,Object>();
20                     for(EcontractTemplateParamAdvanced replaceParams : aTemplateParamList)
21                     {
22                         if(originParam.containsKey(replaceParams.getFieldName()))
23                         {
24                             //TODO 调用参数替换规则脚本      未完待续....
25                             resultMap.put(replaceParams.getAliasField(), originParam.get(replaceParams.getFieldName()));
26                         }
27                     }
28                     resultMap.put("templateName", templateName);
29                     resultMap.put("contract_num", contractNum);
30                     resultMap.put("contract_type", contractType);
31                     originParamList.add(resultMap);
32                 }
33             }
34         }
35         log.info("替换模板参数成功..");
36         return originParamList;
37     }

1 if(originParam.containsKey(replaceParams.getFieldName()))
2
3 originParam:
4 {payBankNum=/, cwmerchantContactEmail=222@qq.com, masterDay=11, masterContractNum=K18-2000-801-03, depositAccount=/, masterYear=2018, cwmerchantContactCz=/, masterMonth=2, billAccountShareAmount=/, merchantName=还有几天就要放假了呀, year=2018, telephoneormobilephone=18068334025, legalName=222, input28=名字, ddmerchantContactTelphone=18068334025, ddmerchantContactEmail=222@qq.com, bankName=/, cwmerchantContactName=名字, cwmerchantContactTelphone=18068334025, input3=广西壮族自治区贵港市, input5=3333, ddmerchantContactName=名字, input6=3333, input20=2018年 02月 11日, month=2, ddmerchantContactCz=/, merechantURL=/, input23=2019年 02月 10日, day=11, contract_num=K18-2000-801-03, templateName=dsmainfinancialword.docx, accessType=首选, bankAccount=/}
5
6
7 {$INPUT19$=/, $INPUT1$=K18-2000-801-03, $INPUT3$=广西壮族自治区贵港市, $INPUT7$=名字, $INPUT17$=/, $INPUT23$=2019年 02月 10日, $INPUT5$=3333, contract_type=0, $INPUT15$=/, $INPUT29$=222, $INPUT11$=名字, $INPUT13$=/, $INPUT27$=/, $INPUT9$=/, $INPUT18$=/, $INPUT2$=还有几天就要放假了呀, $INPUT4$=18068334025, $INPUT6$=3333, $INPUT8$=18068334025, $INPUT16$=/, $INPUT14$=222@qq.com, $INPUT20$=2018年 02月 11日, $INPUT10$=222@qq.com, $INPUT28$=名字, $INPUT26$=首选, contract_num=K18-2000-801-03, $INPUT12$=18068334025, templateName=dsmainfinancialword.docx}
8
9 替换的原理就是把上面获取到的所有的值,根据数据库中的配置,全部提花成$input$开头的,因为word文档的每个参数就是这样设置的。

if(originParam.containsKey(replaceParams.getFieldName()))

originParam:
{payBankNum=/, cwmerchantContactEmail=222@qq.com, masterDay=11, masterContractNum=K18-2000-801-03, depositAccount=/, masterYear=2018, cwmerchantContactCz=/, masterMonth=2, billAccountShareAmount=/, merchantName=还有几天就要放假了呀, year=2018, telephoneormobilephone=18068334025, legalName=222, input28=名字, ddmerchantContactTelphone=18068334025, ddmerchantContactEmail=222@qq.com, bankName=/, cwmerchantContactName=名字, cwmerchantContactTelphone=18068334025, input3=广西壮族自治区贵港市, input5=3333, ddmerchantContactName=名字, input6=3333, input20=2018年 02月 11日, month=2, ddmerchantContactCz=/, merechantURL=/, input23=2019年 02月 10日, day=11, contract_num=K18-2000-801-03, templateName=dsmainfinancialword.docx, accessType=首选, bankAccount=/}

{$INPUT19$=/, $INPUT1$=K18-2000-801-03, $INPUT3$=广西壮族自治区贵港市, $INPUT7$=名字, $INPUT17$=/, $INPUT23$=2019年 02月 10日, $INPUT5$=3333, contract_type=0, $INPUT15$=/, $INPUT29$=222, $INPUT11$=名字, $INPUT13$=/, $INPUT27$=/, $INPUT9$=/, $INPUT18$=/, $INPUT2$=还有几天就要放假了呀, $INPUT4$=18068334025, $INPUT6$=3333, $INPUT8$=18068334025, $INPUT16$=/, $INPUT14$=222@qq.com, $INPUT20$=2018年 02月 11日, $INPUT10$=222@qq.com, $INPUT28$=名字, $INPUT26$=首选, contract_num=K18-2000-801-03, $INPUT12$=18068334025, templateName=dsmainfinancialword.docx}

替换的原理就是把上面获取到的所有的值,根据数据库中的配置,全部提花成$input$开头的,因为word文档的每个参数就是这样设置的。

2.4,替换word文档中的各个参数并合并word文档

fileName = dealWordHelperService.replaceWordText(finalOriginParamList,templeteFilePath,outFilePath);
templeteFilePath:模板存放的地方
outFilePath:替换掉参数临时的路径

1 templateName = (String)templateParam.get("templateName");
2                     log.info("======开始替换模板参数=replaceWordText中==templateName==="+templateName+"=====");
3                     OPCPackage pack = POIXMLDocument.openPackage(templeteFile + File.separator + templateName);
4                     document = new XWPFDocument(pack);

OPCPackage pack = POIXMLDocument.openPackage(templeteFile + File.separator + templateName);
document = new XWPFDocument(pack);

获取document对象

2.4.1,替换页脚

templateParam:

1 {payBankNum=/, cwmerchantContactEmail=222@qq.com, masterDay=11, masterContractNum=K18-2000-801-03, depositAccount=/, masterYear=2018, cwmerchantContactCz=/, masterMonth=2, billAccountShareAmount=/, merchantName=还有几天就要放假了呀, year=2018, telephoneormobilephone=18068334025, legalName=222, input28=名字, ddmerchantContactTelphone=18068334025, ddmerchantContactEmail=222@qq.com, bankName=/, cwmerchantContactName=名字, cwmerchantContactTelphone=18068334025, input3=广西壮族自治区贵港市, input5=3333, ddmerchantContactName=名字, input6=3333, input20=2018年 02月 11日, month=2, ddmerchantContactCz=/, merechantURL=/, input23=2019年 02月 10日, day=11, contract_num=K18-2000-801-03, templateName=dsmainfinancialword.docx, accessType=首选, bankAccount=/}

OperatorWordUtil.replaceInFoot(document,templateParam);

 1 public static void replaceInFoot(XWPFDocument doc, Map<String, Object> params)2     {3         List<XWPFFooter> footerList = doc.getFooterList();4         if (footerList != null && footerList.size() > 0) 5         {6             for (XWPFFooter xWPFFooter : footerList) 7             {8                 String text = xWPFFooter.getText();9                 LOGGER.info("记得删除,遍历的text"+text);
10                 System.out.println("控制台记得删除,遍历的text"+text);
11                 List<XWPFParagraph> xWPFParagraphs = xWPFFooter.getParagraphs();
12                 if (xWPFParagraphs != null && xWPFParagraphs.size() > 0)
13                 {
14                     for (XWPFParagraph xWPFParagraph : xWPFParagraphs)
15                     {
16                         replaceInPara(xWPFParagraph, params);
17                     }
18                 }
19             }
20         }
21     }

 1 public static void replaceInPara(XWPFParagraph paragraph, Map<String, Object> replaceMap) 2     {3         List<XWPFRun> runs = paragraph.getRuns();4         for (int i = 0; i < runs.size(); i++) 5         {6             if (null == runs.get(i))7             {8                 continue;9             }
10             String oneparaString = runs.get(i).getText(runs.get(i).getTextPosition());
11             if (StringUtils.isEmpty(oneparaString))
12             {
13                 continue;
14             }
15             for (Map.Entry<String, Object> entry : replaceMap.entrySet())
16             {
17                 if(null != entry.getValue())
18                 {
19                         oneparaString = oneparaString.replace(entry.getKey().trim(), (String.valueOf(entry.getValue())!=null? String.valueOf(entry.getValue()): ""));
20                 }
21                 else
22                 {
23                     oneparaString = oneparaString.replace(entry.getKey().trim(), "");
24                 }
25             }
26             if(oneparaString!=null && oneparaString.indexOf(breakFlag)>-1){//需要换行
27                 LOGGER.info("----->需要手动换行的"+oneparaString);
28                 String[] breaks = oneparaString.split(breakFlag);
29                 int index = 0;
30                 for(String breakinfo:breaks){
31                     runs.get(i).setText(breakinfo, index);
32                     runs.get(i).addBreak();//换行
33                     index++;
34                 }
35             }else{//不用换行,根据模板正常显示,原来的逻辑
36                 runs.get(i).setText(oneparaString, 0);
37             }
38         }
39     }

2.4.2,替换字段里的参数

 1 public static void replaceInPara(XWPFDocument doc, Map<String, Object> params)2     {3         Iterator<XWPFParagraph> iterator = doc.getParagraphsIterator();4         XWPFParagraph para;5         while (iterator.hasNext()) 6         {7             para = iterator.next();8             replaceInPara(para, params);9         }
10     }

replaceInPara还是上面的方法

2.4.3,替换表格里面的变量

 1 public static void replaceInTable(XWPFDocument doc, Map<String, Object> params) 2     {3         4         Iterator<XWPFTable> iterator = doc.getTablesIterator();5         XWPFTable table;6         List<XWPFTableRow> rows;7         List<XWPFTableCell> cells;8         List<XWPFParagraph> paras;9         while (iterator.hasNext())
10         {
11             table = iterator.next();
12             rows = table.getRows();
13             for (XWPFTableRow row : rows)
14             {
15                 cells = row.getTableCells();
16                 for (XWPFTableCell cell : cells)
17                 {
18                     paras = cell.getParagraphs();
19                     for (XWPFParagraph para : paras)
20                     {
21                        replaceInPara(para, params);
22                     }
23                 }
24              }
25          }
26      }

replaceInPara还是上面的方法

一段XWPFParagraph一段的去读

读到每一段之后他会在吧这一段进行按照word文档当初设置的格式,假如是黏贴复制的话不会换行String oneparaString = runs.get(i).getText(runs.get(i).getTextPosition()),假如是手写的的话就会换行,所以要想把自己的变量单独的取出来必须手动的输入这个变量才会遍历的时候单独的把这个字段取出来。

一个段落就是一个XWPFParagraph,注意这个段落就是我们传统意义上的段落比如

一行就是String oneparaString = runs.get(i).getText(runs.get(i).getTextPosition()),但是行就不是我们传统意义上的行了,请见下图关于oneparaString的解释。它就是一行行的读的。眼睛看上去应该是一行,其实是三行,因为在写好的基础上,我们手动的添加了一个$INPUT6$,一定注意是手动,而不是黏贴复制的。

假如是表格的话,就会在每个单元格算一个段落,word文档中有时候表格是设置的,我们眼睛有时候看不到的,比如每个单元格就是一个cell,其实就是一个段落。

 paras = cell.getParagraphs(); cell

在在单元格的基础上进行一行行的读,读的规则和上面的oneparaString 规则是一样的。

2.5,输出(生成临时的word文档,记得上传服务器之后在删除,否则会积累好多的垃圾数据的)

//生成临时word文件
fileName = newTemWord(outFilePath, templateName, document);

fileNames.append(fileName).append(",");

 1     private String newTemWord(String outFilePath, String templateName,2             XWPFDocument document) throws FileNotFoundException, IOException 3     {4         String outTempFilePath = wordFile + File.separator + outFilePath;5         FileOutputStream outStream = null;6         String fileName = System.currentTimeMillis() + templateName;7         String filePath = outTempFilePath+ File.separator + fileName;8         outStream = new FileOutputStream(filePath);9         document.write(outStream);
10         outStream.close();
11         return fileName;
12     }

2.6,把多个word文档合并成一个word文档

finalWordName = getFinalWordDoc(fileNames.toString(),outFilePath);
log.info("合同生成成功1!合同名称:"+finalWordName);

1     private String getFinalWordDoc(String fileNames,String outFilePath)
2     {
3         String outTempFilePath = wordFile + File.separator + outFilePath;
4         String finalWordName = OperatorWordUtil.mergeWordDocx(outTempFilePath, fileNames);
5         return finalWordName;
6     }

 1 public static String  mergeWordDocx(String outTempFilePath,String fileNames)2     {3         String finalWordName = "";4         if(fileNames.length() > 0 && fileNames != null)5         {6             FileInputStream InStream = null;7             String filePath = "";8             List<InputStream> InputStreams = new ArrayList<InputStream>();9             String[] fileNameArray = fileNames.toString().split(",");
10             InputStream InputStream = null;
11             OutputStream  outputStream = null;
12             try
13             {
14                 for(String fileName : fileNameArray)
15                 {
16                     filePath = outTempFilePath + File.separator + fileName;
17                     InStream = new FileInputStream(filePath);
18                     InputStreams.add(InStream);
19                 }
20
21                 //合并word
22                 InputStream = mergeDocx(InputStreams,outTempFilePath);
23                 finalWordName = System.currentTimeMillis()+".docx";
24                 outputStream = new FileOutputStream(outTempFilePath+File.separator+finalWordName);
25                 int bytesWritten = 0;
26                 int byteCount = 0;
27                 byte[] bytes = new byte[100000000];
28                 while ((byteCount = InputStream.read(bytes)) != -1)
29                 {
30                     outputStream.write(bytes, bytesWritten, byteCount);
31                     bytesWritten += byteCount;
32                 }
33             }
34             catch (Exception e)
35             {
36                 e.printStackTrace();
37             }
38             finally {
39                 try {
40                     if (null != outputStream) {
41                         outputStream.close();
42                     }
43
44                     if (null != InputStream) {
45                         InputStream.close();
46                     }
47
48                     if (null != InStream) {
49                         InStream.close();
50                     }
51                 } catch (Exception e2) {
52                     LOGGER.error("OperatorWordUtil close stream failed!", e2);
53                 }
54             }
55         }
56         return finalWordName;
57     }

 1 public static InputStream mergeDocx(final List<InputStream> streams,String outTempFilePath) throws Docx4JException, IOException 2     {3         WordprocessingMLPackage target = null;4         File tmpdir = new File(outTempFilePath);5         final File generated = File.createTempFile("generated", ".docx",tmpdir);6         int chunkId = 0;7         Iterator<InputStream> it = streams.iterator();8         while (it.hasNext()) 9         {
10             InputStream is = it.next();
11             if (is != null)
12             {
13                 if (target == null)
14                 {
15                     // Copy first (master) document
16                     OutputStream os = new FileOutputStream(generated);
17                     os.write(IOUtils.toByteArray(is));
18                     os.close();
19
20                     target = WordprocessingMLPackage.load(generated);
21                 }
22                 else
23                 {
24                     // Attach the others (Alternative input parts)
25                     insertDocx(target.getMainDocumentPart(), IOUtils.toByteArray(is), chunkId++);
26                 }
27             }
28         }
29
30         if (target != null)
31         {
32             target.save(generated);
33             return new FileInputStream(generated);
34         }
35         else
36         {
37             return null;
38         }
39     }

 1 private static void insertDocx(MainDocumentPart main, byte[] bytes, int chunkId) 2     {3         try 4         {5             AlternativeFormatInputPart afiPart = new AlternativeFormatInputPart(new PartName("/part" + chunkId + ".docx"));6             afiPart.setContentType(new ContentType(ContentTypes.WORDPROCESSINGML_DOCUMENT));7             afiPart.setBinaryData(bytes);8             Relationship altChunkRel = main.addTargetPart(afiPart);9             CTAltChunk chunk = Context.getWmlObjectFactory().createCTAltChunk();
10             chunk.setId(altChunkRel.getId());
11             main.addObject(chunk);
12         }
13         catch (Exception e)
14         {
15             e.printStackTrace();
16         }
17     }

1     public final static String WORDPROCESSINGML_DOCUMENT = "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml";

原来博客地址   https://www.cnblogs.com/qingruihappy/p/8443403.html

POI生成word文档完整案例及讲解相关推荐

  1. POI生成word文档,包括标题,段落,表格,统计图(非图片格式)

    Apache POI 是用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java程式对Microsoft Office格式档案读和写的功能.POI为"P ...

  2. poi生成word文档,插入图片,echar报表生成到word,word表格

    poi生成word文档,word表格,将echar报表生成到word 项目中用到生成word报表,报表中有表格的合并 .页眉.表格中会有报表图片.然后查找了网上的资料,利用echar生成柱状图,然后已 ...

  3. POI生成word文档,图片显示为空白或不显示

    我想要用java,通过poi实现word文档中插入文字和图片来发送邮箱附件.但是发现在对word操作中,图片是白的,size如果设置小了直接没有图片.  经过百度 参考解决 Java poi 3.8 ...

  4. 关于Apache / poi 生成word文档之后不能正常换行的问题

    近期公司项目有个把文本转成word文档的功能,开始使用io操作输出文件的方式(后缀名是docx),使用手机自带的文档浏览工具打开是没有问题的,但是在电脑上用微软office就打开有问题了,于是找了三方 ...

  5. poi生成word文档后打开读取失败

    电脑安装的office,不论是新创建还是修改之前word模板文件,使用代码poi对其文档修改生成新word就打不开,并提示以下错误: 后台代码也没报错,就是打不开word,网上百度的方法都试了,没解决 ...

  6. Java POI 生成Word文档

    在开发中有时候我们需要导出 word文档.最近因为需要做一个生成word文件的功能.就将这块拿出来和大家分享. 生成word文件和我们写word文档是相同的概念,只不过在这里我们换成了用代码来操作.下 ...

  7. POI生成word文档,再通过spire.doc.free 实现word转pdf

    一.POI实现导出当前页面为word文档 1.导入poi依赖 <dependency><groupId>org.apache.poi</groupId><ar ...

  8. poi java 导出word_java poi 生成word文档并下载

    我使用的是Springboot框架开发的.首先需要在pom.xml文件中引入以下maven包: org.apache.poi poi 3.10-FINAL org.apache.poi poi-oox ...

  9. Java poi 生成word文档并下载

    我使用的是Springboot框架开发的.首先需要在pom.xml文件中引入以下maven包: <dependency><groupId>org.apache.poi</ ...

最新文章

  1. 信用评分如何应用在风控策略中(二)
  2. Unity3D粒子系统碰撞器抑制、反弹
  3. 发布会预告 | 多账号管控与网络安全集中化管理能力升级
  4. linux命令去除重复的值,学习linux的Split 命令-linux下去除重复的命令uniq用法...-linux下join与paste命令的用法_169IT.COM...
  5. Redis源码阅读-Adlist双向链表
  6. 对多用户分时系统最重要_互联网搜索引擎:让你的产品在最显眼的位置摆摊
  7. PHPer 面试指南-扩展阅读资源整理
  8. Centos 添加swap
  9. 批量重命名文件夹和文件名bat脚本
  10. git分支拉取develop分支最新代码
  11. 用C语言打印平行四边形
  12. idea启动springboot项目控制台报错Command line is too long. Shorten command line for xxx or also for Spring B解决
  13. TOP100summit:【分享实录】爆炸式增长的斗鱼架构平台的演进
  14. keepalive (1)
  15. linux得到网卡mac地址,获取 linux 网卡MAC地址(包含编程方法)
  16. 对Slim 框架进行总结
  17. C/C++ 中公认的三个难点
  18. C++ std::pair的用法
  19. winfomlabel 从右边_炒菜时,用左边的燃气灶还是右边的燃气灶?燃气师傅提醒,别弄错...
  20. Gingko Framework:log的使用

热门文章

  1. Glusterfs全局统一命名空间
  2. Springboot+采用协同过滤算法的家政服务平台的设计与实现 毕业设计-附源码260839
  3. 剪不断,理还乱——UML的四种关系
  4. 俄亥俄州立大学计算机专业排名,俄亥俄州立大学CS专业排名2020年
  5. c语言程序压缩解压缩文件夹,【转】使用VC++压缩解压缩文件夹
  6. [笔记]VMware常见问题
  7. 大三、大四期间去实习,要不要签三方协议?
  8. js 页面打印,兼容单个打印和分页批量打印,自定义页眉和简单页脚
  9. 电子电路学习笔记(14)——LDO(低压差线性稳压器)
  10. 计算机专业不用学数学的有什么,大学里不用学数学的专业,不想学高数的看过来,你的专业是哪个...