一、首先制作word模版(这里需要注意的是文件后缀是docx不能是doc),${xxxx}是一会要替换的内容

关于为何必须是docx后缀可以看这篇文章https://www.cnblogs.com/ct-csu/p/8178932.html

二、添加poi所需要的jar包文件,我用的maven对jar包进行管理

三、由于poi自身bug,会出现图片无法显示问题,这里需要自定义一个类继承XWPFDocument类,接下来使用的都是我们自己创建的这个类来操作word对象,这个

类对XWPFDocument进行了继承,所以不用担心会有什么问题

package com.newdo.base;import java.io.IOException;import java.io.InputStream;import org.apache.poi.openxml4j.opc.OPCPackage;import org.apache.poi.xwpf.usermodel.XWPFDocument;/** * word  导出工具类 */public class CustomXWPFDocument extends XWPFDocument{public CustomXWPFDocument(InputStream in) throws IOException {super(in);}public CustomXWPFDocument() {super();}public CustomXWPFDocument(OPCPackage pkg) throws IOException {super(pkg);}}

四、接下来就是导出word的工具类了

package com.newdo.base;import java.io.BufferedInputStream;import java.io.BufferedOutputStream;import java.io.ByteArrayInputStream;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.URLEncoder;import java.util.Iterator;import java.util.List;import java.util.Map;import java.util.regex.Matcher;import java.util.regex.Pattern;import javax.servlet.http.HttpServletResponse;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 com.jacob.activeX.ActiveXComponent;import com.jacob.com.ComThread;import com.jacob.com.Dispatch;/** * word 导出工具类 */public class WordUtils {/** * 用于判断导出的word是否转pdf */public static int judgment = 0;public WordUtils() {judgment = 0;}/** * 根据模板生成word *  * @param path      模板的路径 * @param params    需要替换的参数 * @param tableList 需要插入的参数 * @param fileName  生成word文件的文件名 * @param response */public void getWord(String path, Map<String, Object> params, List<String[]> tableList, String fileName,HttpServletResponse response) throws Exception {String saveRoute = path.substring(0, path.indexOf("\\"));File file = new File(path);InputStream is = new FileInputStream(file);CustomXWPFDocument doc = new CustomXWPFDocument(is);this.replaceInPara(doc, params); // 替换文本里面的变量this.replaceInTable(doc, params, tableList); // 替换表格里面的变量OutputStream os = null;if (judgment == 0) {fileName = java.net.URLDecoder.decode(fileName, "UTF-8");os = new FileOutputStream(saveRoute + "\\word\\" + fileName);doc.write(os);String pdf = fileName.substring(0, fileName.lastIndexOf(".")) + ".pdf"; // 截掉wToPdfChange(saveRoute + "\\word\\" + fileName, saveRoute + "\\word\\" + pdf);try {System.out.println("========================pdf下载开始========================");// path是指欲下载的文件的路径。file = new File(saveRoute + "\\word\\" + pdf);// 取得文件名。String filename = URLEncoder.encode(file.getName(), "utf-8");// 取得文件的后缀名。String ext = filename.substring(filename.lastIndexOf(".") + 1).toUpperCase();// 以流的形式下载文件。is = new BufferedInputStream(new FileInputStream(saveRoute + "\\word\\" + pdf));byte[] buffer = new byte[is.available()];is.read(buffer);// 清空responseresponse.reset();// 设置response的Headerresponse.addHeader("Content-Disposition", "attachment;filename=" + filename);response.addHeader("Content-Length", "" + file.length());os = new BufferedOutputStream(response.getOutputStream());response.setContentType("application/octet-stream");os.write(buffer);os.flush();System.out.println("========================pdf下载结束========================");} catch (IOException ex) {ex.printStackTrace();}} else {//os = response.getOutputStream();            fileName = java.net.URLDecoder.decode(fileName, "UTF-8");            os = new FileOutputStream(saveRoute + "\\word\\" + fileName);response.setHeader("Content-Disposition", "attachment; filename=" + fileName);doc.write(os);judgment = 0;}this.close(os);this.close(is);}/** * word 转 pdf *  * @param wordFile word 的路径 word 的路径 * @param pdfFile  pdf 的路径 */public static void wToPdfChange(String wordFile, String pdfFile) {ActiveXComponent app = null;Dispatch document = null;System.out.println("========================开始转换========================");try {// 打开wordSystem.out.println("开始打开word");app = new ActiveXComponent("Word.Application");// 获得word中所有打开的文档Dispatch documents = app.getProperty("Documents").toDispatch();System.out.println("打开文件: " + wordFile);// 打开文档document = Dispatch.call(documents, "Open", wordFile, false, true).toDispatch();// 如果文件存在的话,不会覆盖,会直接报错,所以我们需要判断文件是否存在File target = new File(pdfFile);if (target.exists()) {target.delete();}System.out.println("另存为: " + pdfFile);Dispatch.call(document, "SaveAs", pdfFile, 17);} catch (Exception e) {System.out.println("转换失败" + e.getMessage());} finally {// 关闭office//app.invoke("Quit", 0);if (document != null) {// 关闭文档Dispatch.call(document, "Close", false);}if (app != null) {app.invoke("Quit", 0);}// 获取系统类型String osName = System.getProperty("os.name");// 判断是系统类型if (osName.toLowerCase().startsWith("win")) {System.out.println(osName);// window系统String killCmd = "taskkill /f /im wps.exe";String killCmd1 = "taskkill /f /im wpscenter.exe";Process p;try {p = Runtime.getRuntime().exec(killCmd);p = Runtime.getRuntime().exec(killCmd1);int runnngStatus = p.waitFor();System.out.println("已杀" + runnngStatus);} catch (IOException e) {e.printStackTrace();System.out.println("转换失败" + e.getMessage());} catch (InterruptedException e) {e.printStackTrace();System.out.println("转换失败" + e.getMessage());}}}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 runsIndex = "";String str = "";for (int i = 0; i < runs.size(); i++) {XWPFRun run = runs.get(i);String runText = run.toString().trim();if (!runText.equals("") && (runText.indexOf("${")!=-1 || runText.equals("}"))){                    if ('$' == runText.charAt(0) && '{' == runText.charAt(1)) {                        start = i;                        runsIndex += i+",";                    }                    if ((start != -1)) {                        str += runText;                    }                    if ('}' == runText.charAt(runText.length() - 1)) {                        if (start != -1) {                            end = i;                            for (int k = start; k <= end; k++) {                                para.removeRun(k);                                k--;                                end--;                            }                        }                    }                }}//for (int i = start; i <= end; i++) {//para.removeRun(i);//i--;//end--;//}            int doInt = 0;            String[] runsIns = runsIndex.split(",");            for (int k=0;k<runsIns.length;k++){                if (!"".equals(str)) {                    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());                                if (((String) str).indexOf("\r") > 0) {                                    // 设置换行                                    String[] text = str.toString().split("\r");                                    para.removeRun(0);                                    for (int f = 0; f < text.length; f++) {                                        if (f == 0) {                                            // 此处不缩进因为word模板已经缩进了。                                            para.createRun().setText(text[f].trim());                                        } else {                                            para.createRun().addCarriageReturn();// 硬回车                                            // 注意:wps换行首行缩进是三个空格符,office要的话可以用                                            // run.addTab();缩进或者四个空格符                                            para.createRun().setText(text[f]);                                        }                                    }                                } else {//para.createRun().setText((String) value);                                    int num = 0;                                    if (doInt==0){                                        num = CFunc.ToInt(runsIns[k]);                                    }else{                                        num = CFunc.ToInt(runsIns[k])+doInt;                                    }                                    para.insertNewRun(num).setText((String) value);                                    doInt++;                                }                                // 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);                                    para.createRun().setText(str, 0);                                    break;                                } catch (Exception e) {                                    e.printStackTrace();                                }                            }                        }                    }                }            }}}/** * 为表格插入数据,行数不够添加新行 * * @param table     需要插入数据的表格 * @param tableList 插入数据集合 */private static void insertTable(XWPFTable table, List<String[]> tableList) {// 创建行,根据需要插入的数据添加新行,不处理表头for (int i = 0; i < tableList.size(); i++) {XWPFTableRow row = table.createRow();}// 遍历表格插入数据List<XWPFTableRow> rows = table.getRows();int length = table.getRows().size();for (int i = 1; i < length - 1; i++) {XWPFTableRow newRow = table.getRow(i);List<XWPFTableCell> cells = newRow.getTableCells();for (int j = 0; j < cells.size(); j++) {XWPFTableCell cell = cells.get(j);String s = tableList.get(i - 1)[j];cell.setText(s);}}}/** * 替换表格里面的变量 *  * @param doc    要替换的文档 * @param params 参数 */private void replaceInTable(CustomXWPFDocument doc, Map<String, Object> params, List<String[]> tableList) {Iterator<XWPFTable> iterator = doc.getTablesIterator();XWPFTable table;List<XWPFTableRow> rows;List<XWPFTableCell> cells;List<XWPFParagraph> paras;while (iterator.hasNext()) {table = iterator.next();//System.out.println("------------>" + table.getRows().size());if (table.getRows().size() > 1) {// 判断表格是需要替换还是需要插入,判断逻辑有$为替换,表格无$为插入if (this.matcher(table.getText()).find()) {rows = table.getRows();for (XWPFTableRow row : rows) {cells = row.getTableCells();for (XWPFTableCell cell : cells) {paras = cell.getParagraphs();for (XWPFParagraph para : paras) {this.replaceInPara(para, params, doc);}}}} else {insertTable(table, tableList); // 插入数据}}}}/** * 正则匹配字符串 * * @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 */public void close(InputStream is) {if (is != null) {try {is.close();} catch (IOException e) {e.printStackTrace();}}}/** * 关闭输出流 * * @param os */public void close(OutputStream os) {if (os != null) {try {os.close();} catch (IOException e) {e.printStackTrace();}}}}

五、word转pdf文件我们需要用到一个jacob.jar(下载地址在文章下面 )的一个包,将该jar包放入项目中的resources文件下的lib中

maven中引入本地jar包

同时需要将jacob中的dll文件放到自己的jdk环境中,将 dll 文件拷贝到 C:\Program Files\Java\jdk1.8.0_191\jre\bin (这是我自己的jdk位置)下,或者拷贝到当前项目运行的 jdk 的目录下的 jre/bin 目录下

这里的文件多少位(×64或×86)是根据自己的电脑来的

完成以上操作word才能正常转换为pdf文件

六、最后就是进行测试了,我是用的是springboot框架,这里放出测试代码

@Override    public void contractPrintDetail(HttpServletRequest request, HttpServletResponse response, Map<String, Object> mapArgu) {    //查询出来的数据    Map<String,Object> map = contractPrintMapper.mySelect(mapArgu);        WordUtils wordUtil = new WordUtils();        Map<String, Object> params = new LinkedHashMap<String, Object>();        List<String[]> testList = new ArrayList<String[]>();        params.put("${DWMC}",map.get("DWMC"));        params.put("${DWDZ}",map.get("DWDZ"));        params.put("${FDDBR}",map.get("FDDBR"));        params.put("${XM}",map.get("XM"));        params.put("${XBMC}",map.get("XBMC"));        params.put("${LXDH}",map.get("LXDH"));        params.put("${JG}",map.get("JG"));        params.put("${ZS}",map.get("ZS"));        params.put("${SFZHM}",map.get("SFZHM"));        params.put("${GWMC}",map.get("GWMC"));        try{            String path = CFunc.clobToString((Clob) map.get("HTMB"));            String name = CFunc.ToString(map.get("XM")) + "_" + CFunc.ToString(map.get("HTLX"))+"合同.docx";            String filename = URLEncoder.encode(name, "utf-8");            response.setContentType(request.getServletContext().getMimeType(filename));            wordUtil.getWord(path, params, testList, filename, response);        }catch (Exception e){            e.printStackTrace();        }

七、最后生成的pdf文档
  

POI官网:https://poi.apache.org/
Apache POI Word 简易示例文档:https://my.oschina.net/skymozn/blog/3189732
JACOB:https://sourceforge.net/projects/jacob-project/?source=typ_redirect

参考链接:
https://www.cnblogs.com/duanrantao/p/8682897.html