最新实用版——JAVA使用POI替换Word模板中指定字符,并可插入图片。

JAVA替换Word模板指定字符,并可插入图片。

在一年之前还之前有写过一篇使用jacob对word插入图片的博客。点击率蛮高8800多次,当然多亏了百度搜索引擎的帮忙??
在这里插入图片描述
但是小编在使用以前写的代码走了一遍流程时,发现代码貌似会报错:
在这里插入图片描述
即使我把这个.dll文件放到指定位置,依然不可取。下面推荐大家一套POI替换指定字符和插入图片的工具类,
如果帮到请你点个赞吧,如果有什么优化建议,也欢迎留言讨论哈??
在这里插入图片描述

我使用的依旧是springboot项目,先导入两个POI依赖

1
2
3
4
5
6
7
8
9
10
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>3.14</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>3.14</version>
</dependency>

首先创建一个继承XWPFDocument的类,写一个创建图片的方法。之后会使用该类进行创建图片(poi创建图片有bug),因为继承了XWPFDocument,所以后续的使用中不会报错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
/**
 * @Author: SONGTIANk
 * @Description: 继承XWPF类,写一个创建图片方法
 * @Date: 2020/11/11 17:42
 * @Version: 1.0
 */
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();
        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("测试");
    }
}

之后就是Word工具类了,看一下注释吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
import org.apache.poi.xwpf.usermodel.*;
import java.io.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Author: SONGTIANk
 * @Description: Word替换字符和插入图片工具类
 * @Date: 2020/11/11 17:16
 * @Version: 1.0
 */
public class WordUtils {<!-- -->

    /**
     * 根据模板生成word
     * @param path     模板的路径
     * @param params   需要替换的参数
     * @param fileName 生成word文件的文件名
     * @tips 此处为了方便而将文件生成在了本地,
     * 你也可以直接将文件放入response 响应头中,调用浏览器的下载器直接下载到本地,
     * 这样更符合业务逻辑,即:数据库或者页面输入数据到word模板中,自动生成word并下载;
     * 也可使用插入图片,替换需要在业务中插入的电子签名,或者盖章的word文档。
     * 当然了,此处的传参就需要多加一个 HttpServletResponse response
     * 文件名写中文会有乱码,具体如何调用浏览器下载和解决乱码问题可参考我的另一篇
     * [简便的Excel导出功能](https://blog.csdn.net/weixin_43238452/article/details/108790379)的解决办法。
     */
    public void getWord(String path, Map<String, Object> params,String fileName) throws Exception {<!-- -->
        File file = new File(path);
        //创建输入流
        InputStream is = new FileInputStream(file);
        //创建doc对象
        CustomXWPFDocument doc = new CustomXWPFDocument(is);
        //替换文本里面的变量
        this.replaceInPara(doc, params);    
        //输出流写入文件
        FileOutputStream out = new FileOutputStream(fileName);
        doc.write(out);
        //关闭输入输出流
        this.close(is);
        out.close();
        System.out.println("<-----生成结束----->");
    }

    /**
     * 替换段落里面的变量
     * @param doc    要替换的文档
     * @param params 参数
     */
    private void replaceInPara(CustomXWPFDocument doc, Map<String, Object> params) {<!-- -->
        Iterator<XWPFParagraph> iterator = doc.getParagraphsIterator();
        XWPFParagraph para;
        while (iterator.hasNext()) {<!-- -->
            para = iterator.next();
            this.replaceInPara(para, params, doc);
        }
    }

    /**
     * 替换段落里面的变量
     * @param para   要替换的段落
     * @param params 参数
     */
    private void replaceInPara(XWPFParagraph para, Map<String, Object> params, CustomXWPFDocument doc) {<!-- -->
        List<XWPFRun> runs;
        Matcher matcher;
        if (this.matcher(para.getParagraphText()).find()) {<!-- -->
            runs = para.getRuns();
            int start = -1;
            int end = -1;
            String str = "";
            for (int i = 0; i < runs.size(); i++) {<!-- -->
                XWPFRun run = runs.get(i);
                String runText = run.toString();
                if ('$' == runText.charAt(0) && '{' == runText.charAt(1)) {<!-- -->
                    start = i;
                }
                if ((start != -1)) {<!-- -->
                    str += runText;
                }
                if ('}' == runText.charAt(runText.length() - 1)) {<!-- -->
                    if (start != -1) {<!-- -->
                        end = i;
                        break;
                    }
                }
            }

            for (int i = start; i <= end; i++) {<!-- -->
                para.removeRun(i);
                i--;
                end--;
            }

            for (Map.Entry<String, Object> entry : params.entrySet()) {<!-- -->
                String key = entry.getKey();
                if (str.indexOf(key) != -1) {<!-- -->
                    Object value = entry.getValue();
                    if (value instanceof String) {<!-- -->
                        str = str.replace(key, value.toString());
                        para.createRun().setText(str, 0);
                        break;
                    } else if (value instanceof Map) {<!-- -->
                        str = str.replace(key, "");
                        Map pic = (Map) value;
                        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 = doc.addPicture(byteInputStream,picType);
                            //doc.createPicture(ind, width , height,para);
                            doc.addPictureData(byteInputStream, picType);
                            doc.createPicture(doc.getAllPictures().size() - 1, width, height, para);
                            para.createRun().setText(str, 0);
                            break;
                        } catch (Exception e) {<!-- -->
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
    /**
     * 正则匹配字符串
     * @param str
     * @return
     */
    private Matcher matcher(String str) {<!-- -->
        Pattern pattern = Pattern.compile("\\$\\{(.+?)\\}", Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(str);
        return matcher;
    }
    /**
     * 根据图片类型,取得对应的图片类型代码
     * @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 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) {<!-- -->
                    e2.getStackTrace();
                }
            }
        }
        return byteArray;
    }
    /**
     * 关闭输入流
     * @param is
     */
    private void close(InputStream is) {<!-- -->
        if (is != null) {<!-- -->
            try {<!-- -->
                is.close();
            } catch (IOException e) {<!-- -->
                e.printStackTrace();
            }
        }
    }
}

word和测试类
在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
  public static void main(String[] args) {<!-- -->
        WordUtils wordUtil=new WordUtils();
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("${position}", "java开发");
        params.put("${name}", "焦糖橙子");
        params.put("${sex}", "男");
        params.put("${eMail}", "[email protected]");

        try{<!-- -->
            Map<String,Object> jpeg1 = new HashMap<String, Object>();
            jpeg1.put("width", 100);
            jpeg1.put("height", 150);
            jpeg1.put("type", "jpg");
            jpeg1.put("content", WordUtils.inputStream2ByteArray(new FileInputStream("D:\\text\\cat.jpg"), true));
            params.put("${jpeg1}",jpeg1);
            Map<String,Object>jpeg2 = new HashMap<String, Object>();
            jpeg2.put("width", 100);
            jpeg2.put("height", 150);
            jpeg2.put("type", "jpg");
            jpeg2.put("content", WordUtils.inputStream2ByteArray(new FileInputStream("D:\\text\\dog.jpg"), true));
            params.put("${jpeg2}",jpeg2);
            //模板文件位置
            String path="D:\\text\\myword.docx";  
            //生成文件位置
            String fileName= new String("D:\\text\\myTest.docx".getBytes("UTF-8"),"iso-8859-1");    
            wordUtil.getWord(path,params,fileName);
        }catch(Exception e){<!-- -->
            e.printStackTrace();
        }
    }

执行结果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
至此工具类结束
欢迎大家留言讨论