dsmain:POI生成word文档完整案例及讲解

一,网上的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();

之后和正文段落一样

注:

  • 表格的一格相当于一个完整的docx文档,只是没有页眉和页脚。里面可以有表格,使用xwpfTableCell.getTables()获取,and so on
  • 在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文档的简单示例

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

  • FileInputStream stream = newFileInputStream("d:\\POI\\sample.doc");
  • XWPFDocument doc = new XWPFDocument(stream);// 创建Word文件
  • for(XWPFParagraph p : doc.getParagraphs())//遍历段落
  • {
  •  System.out.print(p.getParagraphText());
  • }
  • for(XWPFTable table : doc.getTables())//遍历表格
  • {
  •  for(XWPFTableRow row : table.getRows())
  •  {
  •  for(XWPFTableCell cell : row.getTableCells())
  •  {
  •  System.out.print(cell.getText());
  •  }
  •  }
  •  

    1.3,参考三,分段混乱

     

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

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

     

    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,获取模板之后,开始获取里面的参数,这个参数是在数据库中配置的。

     

     

    2.4.2,替换字段里的参数

     

    一行就是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 [xss_clean](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 //合并word22 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) document16 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

     

    相关推荐

    相关文章