PoiDocxDemo【Android将表单数据生成Word文档的方案之二(基于Poi4.0.0),目前只能java生成】

版权声明:本文为HaiyuKing原创文章,转载请注明出处!

前言

这个是《PoiDemo【Android将表单数据生成Word文档的方案之二(基于Poi4.0.0)】》的扩展,上一篇是根据doc模板生成doc文件,这个是根据docx模板生成docx文件。

注意:目前只能java生成,集成到Android项目中,运行报错【暂时未解决】:

Process: com.why.project.poidocxdemo, PID: 13762
    java.lang.NoClassDefFoundError: Failed resolution of: Ljavax/xml/stream/XMLEventFactory;
        at org.apache.poi.openxml4j.opc.internal.marshallers.PackagePropertiesMarshaller.<clinit>(PackagePropertiesMarshaller.java:41)
        at org.apache.poi.openxml4j.opc.OPCPackage.<init>(OPCPackage.java:140)
        at org.apache.poi.openxml4j.opc.ZipPackage.<init>(ZipPackage.java:103)
        at org.apache.poi.openxml4j.opc.OPCPackage.open(OPCPackage.java:298)
        at org.apache.poi.ooxml.util.PackageHelper.open(PackageHelper.java:37)
        at org.apache.poi.xwpf.usermodel.XWPFDocument.<init>(XWPFDocument.java:142)

前期准备

参考《PoiDemo【Android将表单数据生成Word文档的方案之二(基于Poi4.0.0)】

区别在于,模板的占位有所不用【docx模板文件不需要$符号】:

PoiDocxDemo【Android将表单数据生成Word文档的方案之二(基于Poi4.0.0),目前只能java生成】

代码分析

参考《PoiDemo【Android将表单数据生成Word文档的方案之二(基于Poi4.0.0)】

使用步骤

一、项目组织结构图

PoiDocxDemo【Android将表单数据生成Word文档的方案之二(基于Poi4.0.0),目前只能java生成】

注意事项:

1、  导入类文件后需要change包名以及重新import R文件路径

2、  Values目录下的文件(strings.xml、dimens.xml、colors.xml等),如果项目中存在,则复制里面的内容,不要整个覆盖

二、导入步骤

1、将poi相关jar文件导入项目中【Demo采用的是module方式】

引用jar文件参考《【Android Studio安装部署系列】十七、Android studio引用第三方库、jar、so、arr文件

注意:

解析docx文件,需要引用下面的jar文件:

  • poi-4.0.0.jar
  • poi-ooxml-4.0.0.jar
  • poi-ooxml-schemas-4.0.0.jar
  • ooxml-lib目录下的curvesapi-1.05.jar、xmlbeans-3.0.1.jar
  • lib目录下的commons-collections4-4.2.jar
  • 下载的commons-compress-1.18.jar

commons-compress-1.18.jar下载地址:http://commons.apache.org/proper/commons-compress/download_compress.cgi

PoiDocxDemo【Android将表单数据生成Word文档的方案之二(基于Poi4.0.0),目前只能java生成】

2、将制作的模板文件复制到D:/temp目录下

 PoiDocxDemo【Android将表单数据生成Word文档的方案之二(基于Poi4.0.0),目前只能java生成】

3、将PoiUtils.java文件复制到项目中

PoiDocxDemo【Android将表单数据生成Word文档的方案之二(基于Poi4.0.0),目前只能java生成】
package com.why.main;

import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class PoiUtils {
    
    //测试
    public static void main(String[] args) {
        String templetDocPath = "D:/temp/请假单模板1.docx";
        String targetDocPath = "D:/temp/请假单1.docx";
        
        Map<String, Object> dataMap = new HashMap<String, Object>();
        dataMap.put("writeDate", "2018年10月14日");
        dataMap.put("name", "HaiyuKing");
        dataMap.put("dept", "移动开发组");
        dataMap.put("leaveType", "?倒休 √年假 ?事假 ?病假 ?婚假 ?产假 ?其他");
        dataMap.put("leaveReason", "倒休一天。");
        dataMap.put("leaveStartDate", "2018年10月14日上午");
        dataMap.put("leaveEndDate", "2018年10月14日下午");
        dataMap.put("leaveDay", "1");
        dataMap.put("leaveLeader", "同意");
        dataMap.put("leaveDeptLeaderImg", "同意!");
        
        PoiUtils.writeToDocx(templetDocPath,targetDocPath,dataMap);
        
        try {
            PoiUtils.readDocx(targetDocPath);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * 通过XWPFDocument对内容进行访问。对于XWPF文档而言,用这种方式进行读操作更佳。
     * @throws Exception
     */
    public static void readDocx(String templetDocPath) throws Exception {
        InputStream is = new FileInputStream(templetDocPath);
        XWPFDocument doc = new XWPFDocument(is);
        List<XWPFParagraph> paras = doc.getParagraphs();
        for (XWPFParagraph para : paras) {
            //当前段落的属性
            System.out.println("para=="+para.getText());
        }
        //获取文档中所有的表格
        List<XWPFTable> tables = doc.getTables();
        List<XWPFTableRow> rows;
        List<XWPFTableCell> cells;
        for (XWPFTable table : tables) {
            //获取表格对应的行
            rows = table.getRows();
            for (XWPFTableRow row : rows) {
                //获取行对应的单元格
                cells = row.getTableCells();
                for (XWPFTableCell cell : cells) {
                    System.out.println("cell=="+cell.getText());;
                }
            }
        }
        if (is != null) {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 生成一个docx文件
     * @param templetDocPath  模板文件的完整路径
     * @param targetDocPath 生成的目标文件的完整路径
     * @param dataMap 替换的数据*/
    public static void writeToDocx(String templetDocPath, String targetDocPath, Map<String,Object> dataMap){
        try
        {
            //得到模板doc文件的HWPFDocument对象
            InputStream in = new FileInputStream(templetDocPath);
            writeToDocx(in,targetDocPath,dataMap);
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
    }

    /**
     * 生成一个docx文件,主要用于直接读取asset目录下的模板文件,不用先复制到sd卡中
     * @param templetDocInStream  模板文件的InputStream
     * @param targetDocPath 生成的目标文件的完整路径
     * @param dataMap 替换的数据*/
    public static void writeToDocx(InputStream templetDocInStream, String targetDocPath, Map<String,Object> dataMap){
        try
        {
            //得到模板doc文件的HWPFDocument对象
            XWPFDocument HDocx = new XWPFDocument(templetDocInStream);
            //替换段落里面的变量  
            replaceInPara(HDocx, dataMap);  
            //替换表格里面的变量  
            replaceInTable(HDocx, dataMap);  
            
            //写到另一个文件中
            OutputStream os = new FileOutputStream(targetDocPath);  
            //把doc输出到输出流中
            HDocx.write(os);
            os.close();
            templetDocInStream.close();
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }
    
    /** 
    * 替换段落里面的变量 
    * @param doc 要替换的文档 
    * @param params 参数 
    */  
    private static void replaceInPara(XWPFDocument doc, Map<String, Object> params) {  
        Iterator<XWPFParagraph> iterator = doc.getParagraphsIterator();  
        XWPFParagraph para;  
        while (iterator.hasNext()) {  
            para = iterator.next();  
            replaceInPara(para, params);  
        }  
    }
    
    
    
    /**
     * 替换段落里面的变量
     * @param para 要替换的段落
     * @param params 参数
     */
    private static void replaceInPara(XWPFParagraph para, Map<String, Object> params) {
        List<XWPFRun> runs;
        System.out.println("para.getParagraphText()=="+para.getParagraphText());
        
        runs = para.getRuns();
        for (int i=0; i<runs.size(); i++) {
            XWPFRun run = runs.get(i);
            String runText = run.toString();
            System.out.println("runText=="+runText);
            
            // 替换文本内容,将自定义的$xxx$替换成实际文本
            for(Map.Entry<String, Object> entry : params.entrySet())
            {
                runText = runText.replace(entry.getKey(), entry.getValue()+"");
                //直接调用XWPFRun的setText()方法设置文本时,在底层会重新创建一个XWPFRun,把文本附加在当前文本后面,
                //所以我们不能直接设值,需要先删除当前run,然后再自己手动插入一个新的run。
                para.removeRun(i);
                para.insertNewRun(i).setText(runText);
            }
        }
    }
    
    /** 
    * 替换表格里面的变量 
    * @param doc 要替换的文档 
    * @param params 参数 
    */  
   private static void replaceInTable(XWPFDocument doc, Map<String, Object> params) {  
      Iterator<XWPFTable> iterator = doc.getTablesIterator();  
      XWPFTable table;  
      List<XWPFTableRow> rows;  
      List<XWPFTableCell> cells;  
      List<XWPFParagraph> paras;  
      while (iterator.hasNext()) {  
         table = iterator.next();  
         rows = table.getRows();  
         for (XWPFTableRow row : rows) {  
            cells = row.getTableCells();  
            for (XWPFTableCell cell : cells) {  
                paras = cell.getParagraphs();  
                for (XWPFParagraph para : paras) {  
                   replaceInPara(para, params);  
                }  
            }  
         }  
      }  
   }  

}
PoiUtils.java

4、运行

PoiDocxDemo【Android将表单数据生成Word文档的方案之二(基于Poi4.0.0),目前只能java生成】

 

5、效果【生成的docx文件有问题,之前的样式全失效了

PoiDocxDemo【Android将表单数据生成Word文档的方案之二(基于Poi4.0.0),目前只能java生成】

混淆配置

暂无

参考资料

Android使用ApachePOI组件读写Worddoc和docx文件

Android 下使用 Poi 读取高版本excel

Java:封装POI实现word的docx文件的简单模板功能

使用POI读写word docx文件

POI操作word2007(docx)文件的文本和图片

项目demo下载地址

链接:https://pan.baidu.com/s/165hpn3kZssxVvHIF9RNtkQ 提取码:3mmj

PoiDocxDemo【Android将表单数据生成Word文档的方案之二(基于Poi4.0.0),目前只能java生成】

上一篇:EM算法笔记


下一篇:Python中文乱码问题