• Java利用poi生成word(包含插入图片,动态表格,行合并)


    转(小改):

    Java利用poi生成word(包含插入图片,动态表格,行合并)

    Java利用poi生成word(包含插入图片,动态表格,行合并)

    测试模板样式:

     




    Word生成结果:



    图表 2
    需要的jar包:(具体jar可自行去maven下载)
    在这里插入图片描述

    注意:需要严格按照上面版本下载jar包,否则可能出现jar包之间不能匹配的导致代码报错

    各种 jar包都可以在这里下载:

    https://mvnrepository.com/


    Test测试类:

    package p1;
    
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    public class Main {
    
        public static void main(String[] args) throws Exception {
            //需要进行文本替换的信息
            Map<String, Object> data = new HashMap<String, Object>();
            data.put("${date}", "2018-03-06");
            data.put("${name}", "东方明珠");
            data.put("${address}", "华东院");
           /* data.put("${communityvalue}", "");
            data.put("${safetycode}", "华东院");
            data.put("${picture2}", "");
            data.put("${picture3}", "");*/
            data.put("${buildingvalue2}", "华东院");
           /* data.put("${patrolPhoto1}", "");
            data.put("${patrolPhoto2}", "");
            data.put("${buildingvalue3}", "中国");*/
    
            //图片,如果是多个图片,就新建多个map
            Map<String,Object> picture1 = new HashMap<String, Object>();
            picture1.put("width", 100);
            picture1.put("height", 150);
            picture1.put("type", "jpg");
            picture1.put("content", WorderToNewWordUtils.inputStream2ByteArray(new FileInputStream("D:/docTest/p1.jpg"), true));
            data.put("${picture1}",picture1);
    
            //需要进行动态生成的信息
            List<Object> mapList = new ArrayList<Object>();
    
            //第一个动态生成的数据列表
            List<String[]> list01 = new ArrayList<String[]>();
            list01.add(new String[]{"A","11111111111","22","22"});
            list01.add(new String[]{"A","22222222222","33","22"});
            list01.add(new String[]{"B","33333333333","44","22"});
            list01.add(new String[]{"C","44444444444","55","22"});
    
            //第二个动态生成的数据列表
            List<String[]> list02 = new ArrayList<String[]>();
            list02.add(new String[]{"A","11111111111","22","22"});
            list02.add(new String[]{"d","22222222222","33","22"});
            list02.add(new String[]{"B","33333333333","44","22"});
            list02.add(new String[]{"C","44444444444","55","22"});
    
            mapList.add(list01);
            mapList.add(list02);
    
            //需要动态改变表格的位置;第一个表格的位置为0
            int[] placeList = {1,4};
    
            CustomXWPFDocument doc = WorderToNewWordUtils.changWord("D:/docTest/t1.docx",data,mapList,placeList);
            FileOutputStream fopts = new FileOutputStream("D:/呵呵.docx");
            doc.write(fopts);
            fopts.close();
        }
    
    }

    WorderToNewWordUtils类:

    package p1;
    
    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    
    import org.apache.poi.POIXMLDocument;
    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 org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr;
    import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTVMerge;
    import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge;
    
    /**
     * Created by 王景伟 on 2018/12/19.
     */
    public class WorderToNewWordUtils {
        /**
         * 根据模板生成word文档
         * @param inputUrl 模板路径
         * @param textMap 需要替换的文本内容
         * @param mapList 需要动态生成的内容
         * @return
         */
        public static CustomXWPFDocument changWord(String inputUrl, Map<String, Object> textMap, List<Object> mapList,int[] placeList) {
            CustomXWPFDocument document = null;
            try {
                //获取docx解析对象
    
                document = new CustomXWPFDocument(POIXMLDocument.openPackage(inputUrl));
                //解析替换文本段落对象
                WorderToNewWordUtils.changeText(document, textMap);
    
                //解析替换表格对象
                WorderToNewWordUtils.changeTable(document, textMap, mapList,placeList);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return document;
        }
    
        /**
         * 替换段落文本
         * @param document docx解析对象
         * @param textMap 需要替换的信息集合
         */
        public static void changeText(CustomXWPFDocument document, Map<String, Object> textMap){
            //获取段落集合
            List<XWPFParagraph> paragraphs = document.getParagraphs();
    
            for (XWPFParagraph paragraph : paragraphs) {
                //判断此段落时候需要进行替换
                String text = paragraph.getText();
                if(checkText(text)){
                    List<XWPFRun> runs = paragraph.getRuns();
                    for (XWPFRun run : runs) {
                        //替换模板原来位置
                        Object ob = changeValue(run.toString(), textMap);
                        System.out.println("段落:"+run.toString());
                        if (ob instanceof String){
                            run.setText((String)ob,0);
                        }
                    }
                }
            }
        }
    
        /**
         * 替换表格对象方法
         * @param document docx解析对象
         * @param textMap 需要替换的信息集合
         * @param mapList 需要动态生成的内容
         */
        public static void changeTable(CustomXWPFDocument document, Map<String, Object> textMap, List<Object> mapList,int[] placeList){
            //获取表格对象集合
            List<XWPFTable> tables = document.getTables();
    
            //循环所有需要进行替换的文本,进行替换
            for (int i = 0; i < tables.size(); i++) {
                XWPFTable table = tables.get(i);
                if(checkText(table.getText())){
                    List<XWPFTableRow> rows = table.getRows();
                    System.out.println("简单表格替换:"+rows);
                    //遍历表格,并替换模板
                    eachTable(document,rows, textMap);
                }
            }
    
            int index=0;
            //操作word中的表格
            for (int i = 0; i < tables.size(); i++) {
                //只处理行数大于等于2的表格,且不循环表头
                XWPFTable table = tables.get(i);
                if(placeList[index]==i){
                    List<String[]> list = (List<String[]>) mapList.get(index);
                    //第二个表格使用daList,插入数据
                    if (null != list && 0 < list.size()){
                        insertTable(table, null,list,2);
                        List<Integer[]> indexList = startEnd(list);
                        for (int c=0;c<indexList.size();c++){
                            //合并行
                            mergeCellVertically(table,0,indexList.get(c)[0]+1,indexList.get(c)[1]+1);
                        }
                    }
                    index++;
                }
    
            }
        }
        /**
         * 遍历表格
         * @param rows 表格行对象
         * @param textMap 需要替换的信息集合
         */
        public static void eachTable(CustomXWPFDocument document, List<XWPFTableRow> rows , Map<String, Object> textMap){
            for (XWPFTableRow row : rows) {
                List<XWPFTableCell> cells = row.getTableCells();
                for (XWPFTableCell cell : cells) {
                    //判断单元格是否需要替换
                    if(checkText(cell.getText())){
                        List<XWPFParagraph> paragraphs = cell.getParagraphs();
                        for (XWPFParagraph paragraph : paragraphs) {
                            List<XWPFRun> runs = paragraph.getRuns();
                            for (XWPFRun run : runs) {
                                Object ob = changeValue(run.toString(), textMap);
                                if (ob instanceof String){
                                    run.setText((String)ob,0);
                                }else if (ob instanceof Map){
                                    run.setText("",0);
                                    Map pic = (Map)ob;
                                    int width = Integer.parseInt(pic.get("width").toString());
                                    int height = Integer.parseInt(pic.get("height").toString());
                                    int picType = getPictureType(pic.get("type").toString());
                                    byte[] byteArray = (byte[]) pic.get("content");
                                    ByteArrayInputStream byteInputStream = new ByteArrayInputStream(byteArray);
                                    try {
                                        int ind = document.addPicture(byteInputStream,picType);
                                        document.createPicture(ind, width , height,paragraph);
                                    } catch (Exception e) {
                                        e.printStackTrace();
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    
        /**
         * 为表格插入数据,行数不够添加新行
         * @param table 需要插入数据的表格
         * @param tableList 第四个表格的插入数据
         * @param daList 第二个表格的插入数据
         * @param type 表格类型:1-第一个表格 2-第二个表格 3-第三个表格 4-第四个表格
         */
        public static void insertTable(XWPFTable table, List<String> tableList,List<String[]> daList,Integer type){
            if (2 == type){
                //创建行和创建需要的列
                for(int i = 1; i < daList.size(); i++){
                    //添加一个新行
                    XWPFTableRow row = table.insertNewTableRow(1);
                    for(int k=0; k<daList.get(0).length;k++){
                        row.createCell();//根据String数组第一条数据的长度动态创建列
                    }
                }
    
                //创建行,根据需要插入的数据添加新行,不处理表头
                for(int i = 0; i < daList.size(); i++){
                    List<XWPFTableCell> cells = table.getRow(i+1).getTableCells();
                    for(int j = 0; j < cells.size(); j++){
                        XWPFTableCell cell02 = cells.get(j);
                        cell02.setText(daList.get(i)[j]);
                    }
                }
            }else if (4 == type){
                //插入表头下面第一行的数据
                for(int i = 0; i < tableList.size(); i++){
                    XWPFTableRow row = table.createRow();
                    List<XWPFTableCell> cells = row.getTableCells();
                    cells.get(0).setText(tableList.get(i));
                }
            }
        }
    
        /**
         * 判断文本中时候包含$
         * @param text 文本
         * @return 包含返回true,不包含返回false
         */
        public static boolean checkText(String text){
            boolean check  =  false;
            if(text.indexOf("$")!= -1){
                check = true;
            }
            return check;
        }
    
        /**
         * 匹配传入信息集合与模板
         * @param value 模板需要替换的区域
         * @param textMap 传入信息集合
         * @return 模板需要替换区域信息集合对应值
         */
        public static Object changeValue(String value, Map<String, Object> textMap){
            Set<Map.Entry<String, Object>> textSets = textMap.entrySet();
            Object valu = "";
            for (Map.Entry<String, Object> textSet : textSets) {
                //匹配模板与替换值 格式${key}
                String key = textSet.getKey();
                if(value.indexOf(key)!= -1){
                    valu = textSet.getValue();
                }
            }
            return valu;
        }
    
        /**
         * 将输入流中的数据写入字节数组
         * @param in
         * @return
         */
        public static byte[] inputStream2ByteArray(InputStream in, boolean isClose){
            byte[] byteArray = null;
            try {
                int total = in.available();
                byteArray = new byte[total];
                in.read(byteArray);
            } catch (IOException e) {
                e.printStackTrace();
            }finally{
                if(isClose){
                    try {
                        in.close();
                    } catch (Exception e2) {
                        System.out.println("关闭流失败");
                    }
                }
            }
            return byteArray;
        }
    
        /**
         * 根据图片类型,取得对应的图片类型代码
         * @param picType
         * @return int
         */
        private static int getPictureType(String picType){
            int res = CustomXWPFDocument.PICTURE_TYPE_PICT;
            if(picType != null){
                if(picType.equalsIgnoreCase("png")){
                    res = CustomXWPFDocument.PICTURE_TYPE_PNG;
                }else if(picType.equalsIgnoreCase("dib")){
                    res = CustomXWPFDocument.PICTURE_TYPE_DIB;
                }else if(picType.equalsIgnoreCase("emf")){
                    res = CustomXWPFDocument.PICTURE_TYPE_EMF;
                }else if(picType.equalsIgnoreCase("jpg") || picType.equalsIgnoreCase("jpeg")){
                    res = CustomXWPFDocument.PICTURE_TYPE_JPEG;
                }else if(picType.equalsIgnoreCase("wmf")){
                    res = CustomXWPFDocument.PICTURE_TYPE_WMF;
                }
            }
            return res;
        }
    
        /**
         * 合并行
         * @param table
         * @param col 需要合并的列
         * @param fromRow 开始行
         * @param toRow 结束行
         */
        public static void mergeCellVertically(XWPFTable table, int col, int fromRow, int toRow) {
            for(int rowIndex = fromRow; rowIndex <= toRow; rowIndex++){
                CTVMerge vmerge = CTVMerge.Factory.newInstance();
                if(rowIndex == fromRow){
                    vmerge.setVal(STMerge.RESTART);
                } else {
                    vmerge.setVal(STMerge.CONTINUE);
                }
                XWPFTableCell cell = table.getRow(rowIndex).getCell(col);
                CTTcPr tcPr = cell.getCTTc().getTcPr();
                if (tcPr != null) {
                    tcPr.setVMerge(vmerge);
                } else {
                    tcPr = CTTcPr.Factory.newInstance();
                    tcPr.setVMerge(vmerge);
                    cell.getCTTc().setTcPr(tcPr);
                }
            }
        }
        /**
         * 获取需要合并单元格的下标
         * @return
         */
        public static List<Integer[]> startEnd(List<String[]> daList){
            List<Integer[]> indexList = new ArrayList<Integer[]>();
    
            List<String> list = new ArrayList<String>();
            for (int i=0;i<daList.size();i++){
                list.add(daList.get(i)[0]);
            }
            Map<Object, Integer> tm = new HashMap<Object, Integer>();
            for (int i=0;i<daList.size();i++){
                if (!tm.containsKey(daList.get(i)[0])) {
                    tm.put(daList.get(i)[0], 1);
                } else {
                    int count = tm.get(daList.get(i)[0]) + 1;
                    tm.put(daList.get(i)[0], count);
                }
            }
            for (Map.Entry<Object, Integer> entry : tm.entrySet()) {
                String key = entry.getKey().toString();
                String value = entry.getValue().toString();
                if (list.indexOf(key) != (-1)){
                    Integer[] index = new Integer[2];
                    index[0] = list.indexOf(key);
                    index[1] = list.lastIndexOf(key);
                    indexList.add(index);
                }
            }
            return indexList;
        }
    
    }

    CustomXWPFDocument类:

    package p1;
    import java.io.IOException;
    import java.io.InputStream;
    
    import org.apache.poi.openxml4j.opc.OPCPackage;
    import org.apache.poi.xwpf.usermodel.XWPFDocument;
    import org.apache.poi.xwpf.usermodel.XWPFParagraph;
    import org.apache.xmlbeans.XmlException;
    import org.apache.xmlbeans.XmlToken;
    import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
    import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
    import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline;
    
    /**
     * Created by 王景伟 on 2018/12/19.
     * 自定义 XWPFDocument,并重写 createPicture()方法
     */
    public class CustomXWPFDocument extends XWPFDocument{
        public CustomXWPFDocument(InputStream in) throws IOException {
            super(in);
        }
    
        public CustomXWPFDocument() {
            super();
        }
    
        public CustomXWPFDocument(OPCPackage pkg) throws IOException {
            super(pkg);
        }
    
        /**
         * @param id
         * @param width 宽
         * @param height 高
         * @param paragraph  段落
         */
        public void createPicture(int id, int width, int height,XWPFParagraph paragraph) {
            final int EMU = 9525;
            width *= EMU;
            height *= EMU;
            String blipId = getAllPictures().get(id).getPackageRelationship().getId();
            CTInline inline = paragraph.createRun().getCTR().addNewDrawing().addNewInline();
    
            System.out.println(blipId+":"+inline);
    
            String picXml = ""
                    + "<a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">"
                    + "   <a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture">"
                    + "      <pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture">"
                    + "         <pic:nvPicPr>" + "            <pic:cNvPr id=""
                    + id
                    + "" name="Generated"/>"
                    + "            <pic:cNvPicPr/>"
                    + "         </pic:nvPicPr>"
                    + "         <pic:blipFill>"
                    + "            <a:blip r:embed=""
                    + blipId
                    + "" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"/>"
                    + "            <a:stretch>"
                    + "               <a:fillRect/>"
                    + "            </a:stretch>"
                    + "         </pic:blipFill>"
                    + "         <pic:spPr>"
                    + "            <a:xfrm>"
                    + "               <a:off x="0" y="0"/>"
                    + "               <a:ext cx=""
                    + width
                    + "" cy=""
                    + height
                    + ""/>"
                    + "            </a:xfrm>"
                    + "            <a:prstGeom prst="rect">"
                    + "               <a:avLst/>"
                    + "            </a:prstGeom>"
                    + "         </pic:spPr>"
                    + "      </pic:pic>"
                    + "   </a:graphicData>" + "</a:graphic>";
    
            inline.addNewGraphic().addNewGraphicData();
            XmlToken xmlToken = null;
            try {
                xmlToken = XmlToken.Factory.parse(picXml);
            } catch (XmlException xe) {
                xe.printStackTrace();
            }
            inline.set(xmlToken);
    
            inline.setDistT(0);
            inline.setDistB(0);
            inline.setDistL(0);
            inline.setDistR(0);
    
            CTPositiveSize2D extent = inline.addNewExtent();
            extent.setCx(width);
            extent.setCy(height);
    
            CTNonVisualDrawingProps docPr = inline.addNewDocPr();
            docPr.setId(id);
            docPr.setName("图片" + id);
            docPr.setDescr("测试");
        }

       ///测试插入图片功能
        public static void main(String[] args) throws InvalidFormatException, FileNotFoundException, IOException {
            //创建一个word文档对象
            CustomXWPFDocument document = new CustomXWPFDocument();
            //创建一个段落对象
            XWPFParagraph paragraph = document.createParagraph();
            //读取本地的一个图片文件生成pid
            int pid = document.addPicture(new FileInputStream("D:/p1.png"), XWPFDocument.PICTURE_TYPE_PNG);
            //创建一个word图片,并插入到文档中
            document.createPicture(pid, 400, 300,paragraph);
            //将文档对象写成本地文件
            FileOutputStream fos = new FileOutputStream(new File("D:/test.docx"));
            document.write(fos);
            fos.close();
        }
    }

    方法调用:

    1. 导入jar包

    2. 复制以上CustomXWPFDocument类和WorderToNewWordUtils类到项目合适路径(根据自己项目情况判断)

    3. 主类调用方法说明(既以上Test测试类)

    4. 对于一般的字段替换,只需要保持(key,value)的键值对方式赋值替换即可,key和模板占位字符保持一致,例:

      Map<String, Object> data = new HashMap<String, Object>();
      data.put("date","20180306");data.put("{date}", "2018-03-06");data.put("date","20180306");data.put("{name}", “东方明珠”);
      data.put("address","");data.put("{address}", "华东院");data.put("address","");data.put("{communityvalue}", “”);
      data.put("safetycode","");data.put("{safetycode}", "华东院");data.put("safetycode","");data.put("{picture2}", “”);
      data.put("picture3","");data.put("{picture3}", "");data.put("picture3","");data.put("{buildingvalue2}", “华东院”);
      data.put("patrolPhoto1","");data.put("{patrolPhoto1}", "");data.put("patrolPhoto1","");data.put("{patrolPhoto2}", “”);data.put("${buildingvalue3}", “中国”);

    5. 如果需要插入图片,则需要重新创建一个Map集合存储图片数据:例

      Map<String,Object> picture1 = new HashMap<String, Object>();
      picture1.put(“width”, 100);
      picture1.put(“height”, 150);
      picture1.put(“type”, “jpg”);
      picture1.put(“content”, WorderToNewWordUtils.inputStream2ByteArray(new FileInputStream(“D:/timg.jpg”), true));
      data.put("${picture1}",picture1);

    如果有多张图片,则需要创建多个Map保存图片,其中它的属性值应保持不变(如上:width,height,type,content),需要注意的是对于一般字段或者是图片最终都存储在data集合中,如下:

    Map<String, Object> data = new HashMap<String, Object>();
    
    • 1
    1. 对于需要根据具体数据动态生成的表格,我们将数据存储在一个List集合中,如:

      //第一个动态生成的数据列表
      List<String[]> list01 = new ArrayList<String[]>();
      list01.add(new String[]{“A”,“11111111111”,“22”,“22”});
      list01.add(new String[]{“A”,“22222222222”,“33”,“22”});
      list01.add(new String[]{“B”,“33333333333”,“44”,“22”});
      list01.add(new String[]{“C”,“44444444444”,“55”,“22”});

      //第二个动态生成的数据列表
      List<String[]> list02 = new ArrayList<String[]>();
      list02.add(new String[]{“A”,“11111111111”,“22”,“22”});
      list02.add(new String[]{“d”,“22222222222”,“33”,“22”});
      list02.add(new String[]{“B”,“33333333333”,“44”,“22”});
      list02.add(new String[]{“C”,“44444444444”,“55”,“22”});

      如上是两个我们需要根据具体数据动态生成的表格,如list01中数据,当list中String数组的第一个字段相同时,则在创建动态表格时会将这两个单元格合并,其他列数值相同不影响合并。注意:这里的创建的String数组的列数应该和模板的表格列数保持一致。数据保存好之后将list数组统一保存在mapList中,如下:
      List mapList = new ArrayList();
      注意:
      由于无法动态获取需要动态填充的表格,所以我们定义了一个静态数组保存需要动态生成的表格的位置,如下:

      //需要动态改变表格的位置;第一个表格的位置为0

      int[] placeList = {1,4};

    如图1所示,从上至下表格所在位置分别是0-4;而我们需要动态生成的是第二个和第五个,故此处传参{1,4}
    最后调用方法导出word即可;如下:

    CustomXWPFDocument doc = WorderToNewWordUtils.changWord("C:/Users/user/Desktop/test1.docx",data,mapList,placeList);
    FileOutputStream fopts = new FileOutputStream("D:/呵呵.docx");
    doc.write(fopts);
    fopts.close();
    

    在changeWord方法中,第一个参数是模板路径,第二个参数是填充数据,第三个参数是动态表格填充数据,第四个参数是动态表格位置;

    友情提醒:(以下内容若需要可采纳,不需要可跳过)
    使用poi生成Word会发生分段混乱的问题,例如:在操作POI替换word时发现getRuns将我们预设的${product}自动切换成了成了两个部分
    ${product } 或者 product使{ product } 既会出现空格分离字段的情况;建议使用从左往右的顺序进行模板字段填充(既:product使:→{→product→} 而不是 $→{→}→product的方式);

    亲测有效:使用notepad++的xml插件修改模板(保证解析完美)
    (一) 安装notepad++并为其添加插件XML Tools插件(具体步骤自行百度)
    (二) 将修改好的word模板另存为xml格式的文档;如下图所示
    在这里插入图片描述
    (三) 使用notepad++打开xml模板,刚开始打开是一个毫无逻辑可寻的文件,这时我们使用xml tools工具格式化xml文件:如下:
    在这里插入图片描述
    (四) 如上图可看出模板被解析成了多部分,此时我们修改文件,让其保持为一个字段:如下
    在这里插入图片描述

    (五) 保存文档之后用word打开xml文档,然后另存为所需要的docx格式word即可

    create by 王景伟
    2018-12-19

  • 相关阅读:
    win10自带邮箱应用无法查看qq邮箱应用解决办法
    Ubuntu紫色背景颜色代码
    VMware中对Linux虚拟机的网络配置静态IP的配置
    CentOS 7在VMware 12中共享文件看不见的问题?
    C++中让人忽视的左值和右值
    C++ allocator类学习理解
    C++11新特性 -----> 右值引用 &&
    重新认识new
    关于C++中nothrow的某某某
    stopPropagation, preventDefault 和 return false 的区别
  • 原文地址:https://www.cnblogs.com/libin6505/p/10330868.html
Copyright © 2020-2023  润新知