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(); } } |
执行结果



至此工具类结束
欢迎大家留言讨论