背景:在 Word 文档的指定位置插入图片,并将 Word 文档转换为 PDF,可以使用 Apache POI 库来操作 Word 文档
准备工作: 在word文档新建一个属于自己模版,用${}占位符来表示,字段信息就是代码里返回的信息
第一步:引入相关的依赖,依赖没给全的看代码提示自行添加
<!-- poi库 --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>4.1.2</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>4.1.2</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml-schemas</artifactId> <version>4.1.2</version> </dependency> <!-- Apache PDFBox库(用于处理PDF文件) --> <dependency> <groupId>org.apache.pdfbox</groupId> <artifactId>pdfbox</artifactId> <version>2.0.27</version> </dependency> <dependency> <groupId>com.deepoove</groupId> <artifactId>poi-tl</artifactId> <version>1.9.0</version> </dependency>
第二步: 把自己定义好的模版放到项目的resources下面,然后下面就是业务代码和一些工具 类,直接拿来用就可以了
第三步:用到的工具类代码,分别是 替换文本和转换PDF工具类
public class ReplaceWordUtils { /** * 执行文本和图片替换的方法 * * @param data 文本数据 * @param picData 图片数据 * @param outWordFile Word生成地址 */ public static void operateWord(Map<String, Object> data, Map<String, Object> picData, String outWordFile) { try { //指定Word模板文件 FileInputStream is = new FileInputStream(outWordFile); XWPFDocument document = new XWPFDocument(is); if (data.size() > 0) { // 替换掉表格之外的文本(仅限文本) ReplaceWordUtils.changeText(document, data); // 替换表格内的文本对象 ReplaceWordUtils.changeTableText(document, data); } if (picData.size() > 0) { // 替换内容图片 ReplaceWordUtils.changePic(document, picData); // 替换表格内的图片对象 ReplaceWordUtils.changeTablePic(document, picData); } //导出替换后的文件 FileOutputStream out = new FileOutputStream(outWordFile); document.write(out); is.close(); out.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } /*** * @Description :替换段落文本 * @param document docx解析对象 * @param textMap 需要替换的信息集合 * @return void * @Date 2022/11/17 17:22 */ public static void changeText(XWPFDocument document, Map<String, Object> textMap) { // 获取段落集合 Iterator<XWPFParagraph> iterator = document.getParagraphsIterator(); XWPFParagraph paragraph = null; while (iterator.hasNext()) { paragraph = iterator.next(); // 判断此段落是否需要替换 if (checkText(paragraph.getText())) { replaceValue(paragraph, textMap); } } } /*** * @Description :检查文本中是否包含指定的字符(此处为“$”) * @param text * @return boolean * @Date 2022/11/17 17:22 */ public static boolean checkText(String text) { boolean check = false; if (text.contains("$")) { check = true; } return check; } /** * 替换图片 * * @param document * @param picData * @throws Exception */ public static void changePic(XWPFDocument document, Map<String, Object> picData) throws Exception { // 获取段落集合 Iterator<XWPFParagraph> iterator = document.getParagraphsIterator(); XWPFParagraph paragraph; while (iterator.hasNext()) { paragraph = iterator.next(); // 判断此段落是否需要替换 String text = paragraph.getText(); if (checkText(text)) { replacePicValue(paragraph, picData); } } } /*** * @Description :替换表格内的文字 * @param document * @param data * @return void * @Date 2022/11/18 11:29 */ public static void changeTableText(XWPFDocument document, Map<String, Object> data) { // 获取文件的表格 Iterator<XWPFTable> tableList = document.getTablesIterator(); XWPFTable table; List<XWPFTableRow> rows; List<XWPFTableCell> cells; // 循环所有需要进行替换的文本,进行替换 while (tableList.hasNext()) { table = tableList.next(); if (checkText(table.getText())) { rows = table.getRows(); // 遍历表格,并替换模板 for (XWPFTableRow row : rows) { cells = row.getTableCells(); for (XWPFTableCell cell : cells) { // 判断单元格是否需要替换 if (checkText(cell.getText())) { List<XWPFParagraph> paragraphs = cell.getParagraphs(); for (XWPFParagraph paragraph : paragraphs) { replaceValue(paragraph, data); } } } } } } } /*** * @Description :替换表格内图片 * @param document * @param picData * @return void * @Date 2022/11/18 11:29 */ public static void changeTablePic(XWPFDocument document, Map<String, Object> picData) throws Exception { // 获取文件的表格 Iterator<XWPFTable> tableList = document.getTablesIterator(); XWPFTable table; List<XWPFTableRow> rows; List<XWPFTableCell> cells; // 循环所有需要进行替换的文本,进行替换 while (tableList.hasNext()) { table = tableList.next(); if (checkText(table.getText())) { rows = table.getRows(); // 遍历表格,并替换模板 for (XWPFTableRow row : rows) { cells = row.getTableCells(); for (XWPFTableCell cell : cells) { // 判断单元格是否需要替换 if (checkText(cell.getText())) { List<XWPFParagraph> paragraphs = cell.getParagraphs(); for (XWPFParagraph paragraph : paragraphs) { replacePicValue(paragraph, picData); } } } } } } } /*** * @Description :替换内容 * @param paragraph * @param textMap * @return void * @Date 2022/11/18 11:33 */ public static void replaceValue(XWPFParagraph paragraph, Map<String, Object> textMap) { XWPFRun run, nextRun; String runsText; List<XWPFRun> runs = paragraph.getRuns(); for (int i = 0; i < runs.size(); i++) { run = runs.get(i); runsText = run.getText(0); if (runsText.contains("${") || (runsText.contains("$") && runs.get(i + 1).getText(0).substring(0, 1).equals("{"))) { while (!runsText.contains("}")) { nextRun = runs.get(i + 1); runsText = runsText + nextRun.getText(0); //删除该节点下的数据 paragraph.removeRun(i + 1); } Object value = changeValue(runsText, textMap); //判断key在Map中是否存在 if (textMap.containsKey(runsText)) { run.setText(value.toString(), 0); } else { //如果匹配不到,则不修改 run.setText(runsText, 0); } } } } /*** * @Description :替换图片内容 * @param paragraph * @param picData * @return void * @Date 2022/11/18 11:33 */ public static void replacePicValue(XWPFParagraph paragraph, Map<String, Object> picData) throws Exception { List<XWPFRun> runs = paragraph.getRuns(); for (XWPFRun run : runs) { Object value = changeValue(run.toString(), picData); if (picData.containsKey(run.toString())) { //清空内容 run.setText("", 0); FileInputStream is = new FileInputStream((String) value); //图片宽度、高度 int width = Units.toEMU(150), height = Units.toEMU(100); //添加图片信息,段落高度需要在模板中自行调整 run.addPicture(is, XWPFDocument.PICTURE_TYPE_PNG, (String) value, width, height); } } } /*** * @Description :匹配参数 * @param value * @param textMap * @return java.lang.Object * @Date 2022/11/18 11:33 */ public static Object changeValue(String value, Map<String, Object> textMap) { Object valu = ""; for (Map.Entry<String, Object> textSet : textMap.entrySet()) { // 匹配模板与替换值 格式${key} String key = textSet.getKey(); if (value.contains(key)) { valu = textSet.getValue(); } } return valu; } public static File getFileByHttpURL(String path) { String newUrl = path.split("[?]")[0]; String[] suffix = newUrl.split("/"); //得到最后一个分隔符后的名字 String fileName = suffix[suffix.length - 1]; File file = null; InputStream inputStream = null; OutputStream outputStream = null; try { file = File.createTempFile("report", fileName);//创建临时文件 URL urlFile = new URL(newUrl); inputStream = urlFile.openStream(); outputStream = new FileOutputStream(file); int bytesRead = 0; byte[] buffer = new byte[8192]; while ((bytesRead = inputStream.read(buffer, 0, 8192)) != -1) { outputStream.write(buffer, 0, bytesRead); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (null != outputStream) { outputStream.close(); } if (null != inputStream) { inputStream.close(); } } catch (Exception e) { e.printStackTrace(); } } return file; } }
Word转PDF工具类:
public class AsposeUtil { public static boolean getLicense() { boolean result = false; try { String os = System.getProperty("os.name"); if (!os.toLowerCase().startsWith("win")) { // FontSettings fontSettings = new FontSettings(); // fontSettings.setFontsFolder("/usr/share/fonts/chinese" + File.separator, true); } InputStream inputStream = new ClassPathResource("license.xml").getInputStream(); License license = new License(); license.setLicense(inputStream); result = true; } catch (Exception e) { e.printStackTrace(); } return result; } public static boolean docPdf(InputStream inputStream, OutputStream outputStream) throws UnsupportedEncodingException { return exchange2(inputStream, SaveFormat.PDF, outputStream); } public static boolean docPng(InputStream inputStream, HttpServletResponse response) throws UnsupportedEncodingException { return exchange(inputStream, SaveFormat.PNG, response); } private static boolean exchange(InputStream inputStream, int saveFormat, HttpServletResponse response) { boolean flag = false; // 验证License 若不验证则会有水印产生 if (getLicense()) { ServletOutputStream outputStream = null; try { response.setCharacterEncoding(Constants.UTF8); response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); outputStream = response.getOutputStream(); Document doc = new Document(inputStream); if (saveFormat == SaveFormat.PNG) { ImageSaveOptions iso = new ImageSaveOptions(saveFormat); // 设置图片分辨率 iso.setResolution(512); doc.save(outputStream, iso); } else { doc.save(outputStream, saveFormat); } flag = true; } catch (Exception e) { e.printStackTrace(); } finally { IOUtils.closeQuietly(outputStream); IOUtils.closeQuietly(inputStream); } } return flag; } public static boolean exchange2(InputStream inputStream, int saveFormat, OutputStream outputStream) { boolean flag = false; // 验证License 若不验证则会有水印产生 if (getLicense()) { try { Document doc = new Document(inputStream); if (saveFormat == SaveFormat.PNG) { ImageSaveOptions iso = new ImageSaveOptions(saveFormat); // 设置图片分辨率 iso.setResolution(512); doc.save(outputStream, iso); } else { doc.save(outputStream, saveFormat); } flag = true; } catch (Exception e) { e.printStackTrace(); } finally { IOUtils.closeQuietly(outputStream); IOUtils.closeQuietly(inputStream); } } return flag; }
第四步:测试代码
@GetMapping("/get/{id}") public R<?> createPDF(){ //替换文本 Map<String, Object> data = new HashMap<>(); //替换图片 Map<String, Object> picData = new HashMap<>(); data.put("${name}", "张三"); data.put("${sex}", "男"); //指定图片地址 picData.put("${url}", "C:\\Users\\admin\\Desktop\\日落.jpg"); //生成Word,这里的路径就是项目里模版的位置 XWPFDocument doc = WordExportUtil.exportWord07("word/测试.docx", data); outputStream = new FileOutputStream(这里是Word路径地址); doc.write(outputStream); outputStream.close(); //执行替换方法 ReplaceWordUtils.operateWord(data, picData, Word路径地址); FileInputStream fileInputStream = new FileInputStream(Word路径地址); FileOutputStream fileOutputStream = new FileOutputStream(PDF路径地址); //Word转换PDF AsposeUtil.docPdf(fileInputStream, fileOutputStream); }
因为测试的自己本地的图片,如果是自己项目中接口中获取的链接,需要调用ReplaceWordUtils.getFileByHttpURL(链接).getAbsolutePath()方法。
也就是这样:picData.put("${url}", ReplaceWordUtils.getFileByHttpURL(链接).getAbsolutePath());
最后测试的效果图:
测试OK,可以根据自己的需求自定义实现
本人写博文完全是为了自己去巩固一些知识点,做笔记,但同时也希望可以帮助到大家,如果有什么不好的地方,也希望大家见谅!希望大家一起进步!!