前言

⏲️本文浏览时长:约10分钟

次要指标:

1.实现Springboot与aspose-words整合,填充word模板并转化PDF;
2.前端vue整合vue-pdf实现PDF预览及下载

word模板重点(详见图示)

1.单属性赋值
2.List循环赋值
3.图片插入
4.对勾特殊符号插入


干货代码

源码

https://gitee.com/javadog-net/boot-apose.git

文件夹形容
boot-aposejava后盾
vue-apose前端vue

对应工具下载

工具形容地址
aspose-words-19.1word三方库https://download.csdn.net/download/baidu_25986059/85390408
javadog-vue-pdf因原版vue-pdf有兼容谬误,此版本为自己订正自用版https://www.npmjs.com/package/javadog-vue-pdf

后果预览

模板填充前空word模板

代码填充后word模板

web端vue预览的html的pdf

最终填充后下载的pdf


技术波及

♂️后端框架

技术名称参考网站
Spring BootMVC框架https://spring.io/projects/spring-boot
Maven我的项目构建http://maven.apache.org
aspose-words本地依赖word工具包https://download.csdn.net/download/baidu_25986059/85390408
lombokJava库https://projectlombok.org/
hutool工具类http://hutool.mydoc.io

♀️前端框架

技术名称参考网站
VUEMVVM框架https://cn.vuejs.org//
Element UIUI库https://element.eleme.cn/2.0/#/zh-CN
javadog-vue-pdfPDF文件在线预览库(集体修复兼容版)https://www.npmjs.com/package/javadog-vue-pdf
axios基于promise网络申请库http://www.axios-js.com/

注释

尽管节约的工夫有点多,不过磨刀不误砍柴工

前置条件

  • 后盾springboot根底我的项目
  • vue根底我的项目
⭐ 如没有根底代码能够间接下载狗哥Gitee源码

步骤解析

后盾
1.下载对应的aspose-words-19.1-jdk16.jar,退出POM本地依赖

因原版免费且会有水印等不确定因素,间接下载jar包本地依赖或者上传私服

 <!-- 本地依赖 aspose-words-->        <dependency>            <groupId>com.aspose</groupId>            <artifactId>aspose-words</artifactId>            <classifier>jdk16</classifier>            <scope>system</scope>            <version>1.0</version>            <systemPath>${project.basedir}/src/main/resources/lib/aspose-words-19.1-jdk16.jar</systemPath>        </dependency>
2.搁置模板文件到资源门路下

3.controller读取模板文件并填充数据
  1. 读取模板并将输出流转为doc,并设置文件名及返回类型

  1. 定位【照片】书签地位,插入图片

  1. 定位【等级】书签地位,插入对应字符


书签插入参考如下

  • 找到须要插入的图片的中央,鼠标焦点聚焦
  • 点击【插入】找到书签并点击,而后录入书签名,并点击增加
  • 查看书签是否增加胜利

    • 更新doc

      • 将根底数据填充后并转为PDF

    详见如下代码
    package apose.javadog.net.controller;import apose.javadog.net.entity.BaseInfo;import apose.javadog.net.entity.Education;import apose.javadog.net.entity.Interview;import apose.javadog.net.entity.WorkExperience;import cn.hutool.core.util.CharsetUtil;import com.aspose.words.Document;import com.aspose.words.DocumentBuilder;import com.aspose.words.ReportingEngine;import com.aspose.words.SaveFormat;import lombok.extern.slf4j.Slf4j;import org.springframework.core.io.ClassPathResource;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import javax.servlet.ServletOutputStream;import javax.servlet.http.HttpServletResponse;import java.io.InputStream;import java.net.URLEncoder;import java.util.ArrayList;import java.util.List;@RestController@RequestMapping("/word")@Slf4jpublic class WordController { @GetMapping("/pdf") void pdf(HttpServletResponse response){     // 获取资源doc门路下的简历interview.doc模板     final  ClassPathResource classPathResource = new ClassPathResource("doc\\interview.doc");     // 组装数据     final Document doc;     try (InputStream inputStream = classPathResource.getInputStream();          ServletOutputStream outputStream = response.getOutputStream()) {         // 文件名称         String fileName = URLEncoder.encode("帅锅的简历.pdf", CharsetUtil.UTF_8);         response.reset();         response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");         response.setHeader("Access-Control-Allow-Origin", "*");         response.setContentType("application/octet-stream;charset=UTF-8");         // 将输出流转为doc         doc = new Document(inputStream);         // doc构建         DocumentBuilder builder = new DocumentBuilder(doc);         // 定位书签地位         builder.moveToBookmark("AVATAR");         // 插入图片         builder.insertImage("https://portrait.gitee.com/uploads/avatars/user/491/1474070_javadog-net_1616995139.png!avatar30");         // 定位LANGUAGE_LEVEL4书签地位         builder.moveToBookmark("LANGUAGE_LEVEL4");         // 设置字符名称         builder.getFont().setName("Wingdings");         // 设置字符大小         builder.getFont().setSize(14);         // 对号字符         builder.write("\uF0FE");         // 定位LANGUAGE_LEVEL6书签地位         builder.moveToBookmark("LANGUAGE_LEVEL6");         // 设置字符名称         builder.getFont().setName("Wingdings");         builder.getFont().setSize(20);         builder.write("□");         doc.updateFields();         final ReportingEngine engine = new ReportingEngine();         // 将数据填充至模板         engine.buildReport(doc, getInterviewData(), "data");         // 转pdf         doc.save(outputStream, SaveFormat.PDF);     } catch (Exception e) {         log.error("生成报告异样,异样信息:{}", e.getMessage(), e);         e.printStackTrace();     } } private Interview getInterviewData(){     Interview interview = new Interview();     this.getBaseInfo(interview);     this.getEducations(interview);     this.getWorkExperiences(interview);     return interview; } /**  * @Description: 组装根本数据  * @Param: [interview]  * @return: [apose.javadog.net.entity.Interview]  * @Author: hdx  * @Date: 2022/5/10 15:39  */ private void getBaseInfo(Interview interview){     // 根本数据     BaseInfo baseInfo = new BaseInfo();     List<String> listStr = new ArrayList<>();     listStr.add("后端技术栈:有较好的Java根底,相熟SpringBoot,SpringCloud,springCloud Alibaba等支流技术,Redis内存数据库、RocketMq、dubbo等,相熟JUC多线程");     listStr.add("后端模板引擎:thymeleaf、volocity");     listStr.add("前端技术栈:熟练掌握ES5/ES6/、NodeJs、Vue、React、Webpack、gulp");     listStr.add("其余技术栈: 相熟python+selenium、electron");     baseInfo.setName("狗哥")             .setBirth("1993年5月14日")             .setHeight("180")             .setWeight("70")             .setNation("汉")             .setSex("男")             .setNativePlace("济南")             .setMarriage("已婚")             .setSpecialtys(listStr);     interview.setBaseInfo(baseInfo); } /**  * @Description: 组装教育经验  * @Param: [interview]  * @return: [apose.javadog.net.entity.Interview]  * @Author: hdx  * @Date: 2022/5/10 15:40  */ private void getEducations(Interview interview){     // 高中     List<Education> educations = new ArrayList<>();     Education education = new Education();     education.setStartEndTime("2009-2012")             .setSchool("山东省试验中学")             .setFullTime("是")             .setProfessional("文科")             .setEducationalForm("普高");     educations.add(education);     // 大学     Education educationUniversity = new Education();     educationUniversity.setStartEndTime("2012-2016")             .setSchool("青岛农业大学")             .setFullTime("是")             .setProfessional("计算机科学与技术")             .setEducationalForm("本科");     educations.add(educationUniversity);     interview.setEducations(educations); } /**  * @Description: 组装工作经验  * @Param: [interview]  * @return: [apose.javadog.net.entity.Interview]  * @Author: hdx  * @Date: 2022/5/10 15:40  */ private void getWorkExperiences(Interview interview){     // 工作记录     List<WorkExperience> workExperiences = new ArrayList<>();     WorkExperience workExperience = new WorkExperience();     workExperience.setStartEndTime("2009-2012")             .setWorkUnit("青岛XXX")             .setPosition("开发")             .setResignation("有更好的学习空间,向医疗畛域拓展学习纬度");     workExperiences.add(workExperience);     interview.setWorkExperiences(workExperiences); }}
前端
1.下载对应的依赖包
npm install
2.在vue.config.js中配置代理
const { defineConfig } = require('@vue/cli-service')module.exports = defineConfig({  devServer: {    port: 1026,    proxy: {      '/': {        target: 'http://localhost:8082', //申请本地 须要ipps-boot后盾我的项目        ws: false,        changeOrigin: true      }    }  }})
npm install
3.在main.js引入所需插件
import Vue from 'vue'import App from './App.vue'Vue.config.productionTip = falseimport axios from 'axios'Vue.prototype.$http = axiosimport ElementUI from 'element-ui';import 'element-ui/lib/theme-chalk/index.css';Vue.use(ElementUI);new Vue({  render: h => h(App),}).$mount('#app')
4.页面引入vue-pdf组件
  <pdf v-if="showPdf" ref="pdf" :src="pdfUrl" :page="currentPage" @num-pages="pageCount=$event"               @page-loaded="currentPage=$event" @loaded="loadPdfHandler">   </pdf>
5.页面中应用axios调取接口获取数据

留神responseType类型为blob

this.$http({      method: 'get',      url: `/word/pdf`,      responseType: 'blob'    }).then(res=>{      console.log(res)      this.pdfUrl = this.getObjectURL(res.data)      console.log(this.pdfUrl)      const loadingTask = pdf.createLoadingTask(this.pdfUrl)      // // 留神这里肯定要这样写      loadingTask.promise.then(load => {        this.numberPage = load.numPages      }).catch(err => {        console.log(err)      })      this.loading = false;    })
页面残缺代码如下
<template>  <div class="pdf_wrap">    <template>      <el-form ref="form" label-width="80px">        <div style='text-align: center;margin: 30px;' v-if="loading">          数据加载中...        </div>        <div v-if="loading==false" style="display: flex;align-items: center;">          <div style="flex: 1;"></div>          <el-button size="mini" @click="changePdfPage(0)" type="primary">上一页</el-button>          <div style="position: relative; margin: 0px 10px; top: -10px;">            {{currentPage}} / {{pageCount}}  共 {{numberPage}} 页          </div>          <el-button size="mini" @click="changePdfPage(1)" type="primary">下一页</el-button>          <el-button size="mini" @click='print' type="primary">打印</el-button>        </div>        <div v-show="loading==false">          <pdf v-if="showPdf" ref="pdf" :src="pdfUrl" :page="currentPage" @num-pages="pageCount=$event"               @page-loaded="currentPage=$event" @loaded="loadPdfHandler">          </pdf>        </div>      </el-form>    </template>  </div></template><script>import pdf from 'javadog-vue-pdf'export default {  components: {    pdf  },  data () {    return {      loading: true,      showPdf: false,      currentPage: 1, // pdf文件页码      pageCount: 1, // pdf文件总页数      fileType: 'pdf', // 文件类型      pdfUrl: '',      numberPage:1    }  },  mounted () {    this.showPdf = true;    this.$http({      method: 'get',      url: `/word/pdf`,      responseType: 'blob'    }).then(res=>{      console.log(res)      this.pdfUrl = this.getObjectURL(res.data)      console.log(this.pdfUrl)      const loadingTask = pdf.createLoadingTask(this.pdfUrl)      // // 留神这里肯定要这样写      loadingTask.promise.then(load => {        this.numberPage = load.numPages      }).catch(err => {        console.log(err)      })      this.loading = false;    })  },  methods: {    print(){      this.$refs.pdf.print(600)    },    getObjectURL(file) {      let url = null      if (window.createObjectURL !== undefined) { // basic        url = window.createObjectURL(file)      } else if (window.webkitURL !== undefined) { // webkit or chrome        // try {        let blob = new Blob([file], {          type: "application/pdf"        });        url = window.URL.createObjectURL(blob)        console.log(url)      } else if (window.URL !== undefined) { // mozilla(firefox)        try {          url = window.URL.createObjectURL(file)        } catch (error) {          console.log(error)        }      }      return url    },    changePdfPage(val) {      console.log(val)      if (val === 0 && this.currentPage > 1) {        this.currentPage--        // console.log(this.currentPage)      }      if (val === 1 && this.currentPage < this.pageCount) {        this.currentPage++        // console.log(this.currentPage)      }    },    // pdf加载时    loadPdfHandler() {      console.log('jiazai')      this.currentPage = 1 // 加载的时候先加载第一页      this.loading = false;    }  }}</script><style scoped>.pdf_wrap {  background: #fff;  height: 100vh;  width: 80vh;  margin: 0 auto;}.pdf_list {  height: 80vh;  overflow: scroll;}button {  margin-bottom: 20px;}</style>

异常情况

1.vue-pdf原版与webpack版本问题,会启动不起来,所以本狗才移花接木,改了一下并自用

2.aspose-words-19.1-jdk16.jar 如果采纳官网的maven依赖,可能须要自助破解或交费应用

成绩展现

JavaDog狗屋地址
集体博客https://blog.javadog.net
公众号https://mp.weixin.qq.com/s/_vgnXoQ8FSobD3OfRAf5gw
CSDNhttps://blog.csdn.net/baidu_25986059
掘金https://juejin.cn/user/2172290706716775
知乎https://www.zhihu.com/people/JavaDog
简书https://www.jianshu.com/u/1ff9c6bdb916
giteehttps://gitee.com/javadog-net
GitHubhttps://github.com/javadog-net