屎上最全vue-pdf+Springboot与aspose-words整合,开箱即用

╰半橙微兮° 2024-05-11 09:05 80阅读 0赞

前言

⏲️本文阅读时长:约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-apose java后台
vue-apose 前端vue
对应工具下载




















工具 描述 地址
aspose-words-19.1 word三方库 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 Boot MVC框架 https://spring.io/projects/spring-boot
Maven 项目构建 http://maven.apache.org
aspose-words 本地依赖word工具包 https://download.csdn.net/download/baidu_25986059/85390408
lombok Java库 https://projectlombok.org/
hutool 工具类 http://hutool.mydoc.io
?♀️前端框架






























技术 名称 参考网站
VUE MVVM框架 https://cn.vuejs.org//
Element UI UI库 https://element.eleme.cn/2.0/#/zh-CN
javadog-vue-pdf PDF文件在线预览库(个人修复兼容版) 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包本地依赖或者上传私服

我还在原地等你

  1. <!-- 本地依赖 aspose-words-->
  2. <dependency>
  3. <groupId>com.aspose</groupId>
  4. <artifactId>aspose-words</artifactId>
  5. <classifier>jdk16</classifier>
  6. <scope>system</scope>
  7. <version>1.0</version>
  8. <systemPath>${project.basedir}/src/main/resources/lib/aspose-words-19.1-jdk16.jar</systemPath>
  9. </dependency>
2.放置模板文件到资源路径下

在这里插入图片描述

3.controller读取模板文件并填充数据
  1. 读取模板并将输入流转为doc,并设置文件名及返回类型
    在这里插入图片描述
  2. 定位【照片】书签位置,插入图片
    在这里插入图片描述
  3. 定位【等级】书签位置,插入对应字符
    在这里插入图片描述
    书签插入参考如下

    • 找到需要插入的图片的地方,鼠标焦点聚焦
      在这里插入图片描述
    • 点击【插入】找到书签并点击,然后录入书签名,并点击添加
      在这里插入图片描述
    • 检查书签是否添加成功
    • 更新doc
      \-
    • 将基础数据填充后并转为PDF
      在这里插入图片描述

详见如下代码

  1. package apose.javadog.net.controller;
  2. import apose.javadog.net.entity.BaseInfo;
  3. import apose.javadog.net.entity.Education;
  4. import apose.javadog.net.entity.Interview;
  5. import apose.javadog.net.entity.WorkExperience;
  6. import cn.hutool.core.util.CharsetUtil;
  7. import com.aspose.words.Document;
  8. import com.aspose.words.DocumentBuilder;
  9. import com.aspose.words.ReportingEngine;
  10. import com.aspose.words.SaveFormat;
  11. import lombok.extern.slf4j.Slf4j;
  12. import org.springframework.core.io.ClassPathResource;
  13. import org.springframework.web.bind.annotation.GetMapping;
  14. import org.springframework.web.bind.annotation.RequestMapping;
  15. import org.springframework.web.bind.annotation.RestController;
  16. import javax.servlet.ServletOutputStream;
  17. import javax.servlet.http.HttpServletResponse;
  18. import java.io.InputStream;
  19. import java.net.URLEncoder;
  20. import java.util.ArrayList;
  21. import java.util.List;
  22. @RestController
  23. @RequestMapping("/word")
  24. @Slf4j
  25. public class WordController {
  26. @GetMapping("/pdf")
  27. void pdf(HttpServletResponse response){
  28. // 获取资源doc路径下的简历interview.doc模板
  29. final ClassPathResource classPathResource = new ClassPathResource("doc\\interview.doc");
  30. // 组装数据
  31. final Document doc;
  32. try (InputStream inputStream = classPathResource.getInputStream();
  33. ServletOutputStream outputStream = response.getOutputStream()) {
  34. // 文件名称
  35. String fileName = URLEncoder.encode("帅锅的简历.pdf", CharsetUtil.UTF_8);
  36. response.reset();
  37. response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
  38. response.setHeader("Access-Control-Allow-Origin", "*");
  39. response.setContentType("application/octet-stream;charset=UTF-8");
  40. // 将输入流转为doc
  41. doc = new Document(inputStream);
  42. // doc构建
  43. DocumentBuilder builder = new DocumentBuilder(doc);
  44. // 定位书签位置
  45. builder.moveToBookmark("AVATAR");
  46. // 插入图片
  47. builder.insertImage("https://portrait.gitee.com/uploads/avatars/user/491/1474070_javadog-net_1616995139.png!avatar30");
  48. // 定位LANGUAGE_LEVEL4书签位置
  49. builder.moveToBookmark("LANGUAGE_LEVEL4");
  50. // 设置字符名称
  51. builder.getFont().setName("Wingdings");
  52. // 设置字符大小
  53. builder.getFont().setSize(14);
  54. // 对号字符
  55. builder.write("\uF0FE");
  56. // 定位LANGUAGE_LEVEL6书签位置
  57. builder.moveToBookmark("LANGUAGE_LEVEL6");
  58. // 设置字符名称
  59. builder.getFont().setName("Wingdings");
  60. builder.getFont().setSize(20);
  61. builder.write("□");
  62. doc.updateFields();
  63. final ReportingEngine engine = new ReportingEngine();
  64. // 将数据填充至模板
  65. engine.buildReport(doc, getInterviewData(), "data");
  66. // 转pdf
  67. doc.save(outputStream, SaveFormat.PDF);
  68. } catch (Exception e) {
  69. log.error("生成报告异常,异常信息:{}", e.getMessage(), e);
  70. e.printStackTrace();
  71. }
  72. }
  73. private Interview getInterviewData(){
  74. Interview interview = new Interview();
  75. this.getBaseInfo(interview);
  76. this.getEducations(interview);
  77. this.getWorkExperiences(interview);
  78. return interview;
  79. }
  80. /**
  81. * @Description: 组装基本数据
  82. * @Param: [interview]
  83. * @return: [apose.javadog.net.entity.Interview]
  84. * @Author: hdx
  85. * @Date: 2022/5/10 15:39
  86. */
  87. private void getBaseInfo(Interview interview){
  88. // 基本数据
  89. BaseInfo baseInfo = new BaseInfo();
  90. List<String> listStr = new ArrayList<>();
  91. listStr.add("后端技术栈:有较好的Java基础,熟悉SpringBoot,SpringCloud,springCloud Alibaba等主流技术,Redis内存数据库、RocketMq、dubbo等,熟悉JUC多线程");
  92. listStr.add("后端模板引擎:thymeleaf、volocity");
  93. listStr.add("前端技术栈:熟练掌握ES5/ES6/、NodeJs、Vue、React、Webpack、gulp");
  94. listStr.add("其他技术栈: 熟悉python+selenium、electron");
  95. baseInfo.setName("狗哥")
  96. .setBirth("1993年5月14日")
  97. .setHeight("180")
  98. .setWeight("70")
  99. .setNation("汉")
  100. .setSex("男")
  101. .setNativePlace("济南")
  102. .setMarriage("已婚")
  103. .setSpecialtys(listStr);
  104. interview.setBaseInfo(baseInfo);
  105. }
  106. /**
  107. * @Description: 组装教育经历
  108. * @Param: [interview]
  109. * @return: [apose.javadog.net.entity.Interview]
  110. * @Author: hdx
  111. * @Date: 2022/5/10 15:40
  112. */
  113. private void getEducations(Interview interview){
  114. // 高中
  115. List<Education> educations = new ArrayList<>();
  116. Education education = new Education();
  117. education.setStartEndTime("2009-2012")
  118. .setSchool("山东省实验中学")
  119. .setFullTime("是")
  120. .setProfessional("理科")
  121. .setEducationalForm("普高");
  122. educations.add(education);
  123. // 大学
  124. Education educationUniversity = new Education();
  125. educationUniversity.setStartEndTime("2012-2016")
  126. .setSchool("青岛农业大学")
  127. .setFullTime("是")
  128. .setProfessional("计算机科学与技术")
  129. .setEducationalForm("本科");
  130. educations.add(educationUniversity);
  131. interview.setEducations(educations);
  132. }
  133. /**
  134. * @Description: 组装工作经历
  135. * @Param: [interview]
  136. * @return: [apose.javadog.net.entity.Interview]
  137. * @Author: hdx
  138. * @Date: 2022/5/10 15:40
  139. */
  140. private void getWorkExperiences(Interview interview){
  141. // 工作记录
  142. List<WorkExperience> workExperiences = new ArrayList<>();
  143. WorkExperience workExperience = new WorkExperience();
  144. workExperience.setStartEndTime("2009-2012")
  145. .setWorkUnit("青岛XXX")
  146. .setPosition("开发")
  147. .setResignation("有更好的学习空间,向医疗领域拓展学习纬度");
  148. workExperiences.add(workExperience);
  149. interview.setWorkExperiences(workExperiences);
  150. }
  151. }
前端
1.下载对应的依赖包

npm install

2.在vue.config.js中配置代理
  1. const {
  2. defineConfig } = require('@vue/cli-service')
  3. module.exports = defineConfig({
  4. devServer: {
  5. port: 1026,
  6. proxy: {
  7. '/': {
  8. target: 'http://localhost:8082', //请求本地 需要ipps-boot后台项目
  9. ws: false,
  10. changeOrigin: true
  11. }
  12. }
  13. }
  14. })

npm install

3.在main.js引入所需插件
  1. import Vue from 'vue'
  2. import App from './App.vue'
  3. Vue.config.productionTip = false
  4. import axios from 'axios'
  5. Vue.prototype.$http = axios
  6. import ElementUI from 'element-ui';
  7. import 'element-ui/lib/theme-chalk/index.css';
  8. Vue.use(ElementUI);
  9. new Vue({
  10. render: h => h(App),
  11. }).$mount('#app')
4.页面引入vue-pdf组件
  1. <pdf v-if="showPdf" ref="pdf" :src="pdfUrl" :page="currentPage" @num-pages="pageCount=$event"
  2. @page-loaded="currentPage=$event" @loaded="loadPdfHandler">
  3. </pdf>
5.页面中使用axios调取接口获取数据

?注意responseType类型为blob

  1. this.$http({
  2. method: 'get',
  3. url: `/word/pdf`,
  4. responseType: 'blob'
  5. }).then(res=>{
  6. console.log(res)
  7. this.pdfUrl = this.getObjectURL(res.data)
  8. console.log(this.pdfUrl)
  9. const loadingTask = pdf.createLoadingTask(this.pdfUrl)
  10. // // 注意这里一定要这样写
  11. loadingTask.promise.then(load => {
  12. this.numberPage = load.numPages
  13. }).catch(err => {
  14. console.log(err)
  15. })
  16. this.loading = false;
  17. })

页面完整代码如下

  1. <template>
  2. <div class="pdf_wrap">
  3. <template>
  4. <el-form ref="form" label-width="80px">
  5. <div style='text-align: center;margin: 30px;' v-if="loading">
  6. 数据加载中...
  7. </div>
  8. <div v-if="loading==false" style="display: flex;align-items: center;">
  9. <div style="flex: 1;"></div>
  10. <el-button size="mini" @click="changePdfPage(0)" type="primary">上一页</el-button>
  11. <div style="position: relative; margin: 0px 10px; top: -10px;">
  12. {
  13. {currentPage}} / {
  14. {pageCount}} 共 {
  15. {numberPage}} 页
  16. </div>
  17. <el-button size="mini" @click="changePdfPage(1)" type="primary">下一页</el-button>
  18. <el-button size="mini" @click='print' type="primary">打印</el-button>
  19. </div>
  20. <div v-show="loading==false">
  21. <pdf v-if="showPdf" ref="pdf" :src="pdfUrl" :page="currentPage" @num-pages="pageCount=$event"
  22. @page-loaded="currentPage=$event" @loaded="loadPdfHandler">
  23. </pdf>
  24. </div>
  25. </el-form>
  26. </template>
  27. </div>
  28. </template>
  29. <script>
  30. import pdf from 'javadog-vue-pdf'
  31. export default {
  32. components: {
  33. pdf
  34. },
  35. data () {
  36. return {
  37. loading: true,
  38. showPdf: false,
  39. currentPage: 1, // pdf文件页码
  40. pageCount: 1, // pdf文件总页数
  41. fileType: 'pdf', // 文件类型
  42. pdfUrl: '',
  43. numberPage:1
  44. }
  45. },
  46. mounted () {
  47. this.showPdf = true;
  48. this.$http({
  49. method: 'get',
  50. url: `/word/pdf`,
  51. responseType: 'blob'
  52. }).then(res=>{
  53. console.log(res)
  54. this.pdfUrl = this.getObjectURL(res.data)
  55. console.log(this.pdfUrl)
  56. const loadingTask = pdf.createLoadingTask(this.pdfUrl)
  57. // // 注意这里一定要这样写
  58. loadingTask.promise.then(load => {
  59. this.numberPage = load.numPages
  60. }).catch(err => {
  61. console.log(err)
  62. })
  63. this.loading = false;
  64. })
  65. },
  66. methods: {
  67. print(){
  68. this.$refs.pdf.print(600)
  69. },
  70. getObjectURL(file) {
  71. let url = null
  72. if (window.createObjectURL !== undefined) {
  73. // basic
  74. url = window.createObjectURL(file)
  75. } else if (window.webkitURL !== undefined) {
  76. // webkit or chrome
  77. // try {
  78. let blob = new Blob([file], {
  79. type: "application/pdf"
  80. });
  81. url = window.URL.createObjectURL(blob)
  82. console.log(url)
  83. } else if (window.URL !== undefined) {
  84. // mozilla(firefox)
  85. try {
  86. url = window.URL.createObjectURL(file)
  87. } catch (error) {
  88. console.log(error)
  89. }
  90. }
  91. return url
  92. },
  93. changePdfPage(val) {
  94. console.log(val)
  95. if (val === 0 && this.currentPage > 1) {
  96. this.currentPage--
  97. // console.log(this.currentPage)
  98. }
  99. if (val === 1 && this.currentPage < this.pageCount) {
  100. this.currentPage++
  101. // console.log(this.currentPage)
  102. }
  103. },
  104. // pdf加载时
  105. loadPdfHandler() {
  106. console.log('jiazai')
  107. this.currentPage = 1 // 加载的时候先加载第一页
  108. this.loading = false;
  109. }
  110. }
  111. }
  112. </script>
  113. <style scoped>
  114. .pdf_wrap {
  115. background: #fff;
  116. height: 100vh;
  117. width: 80vh;
  118. margin: 0 auto;
  119. }
  120. .pdf_list {
  121. height: 80vh;
  122. overflow: scroll;
  123. }
  124. button {
  125. margin-bottom: 20px;
  126. }
  127. </style>

异常情况

1.vue-pdf原版与webpack版本问题,会启动不起来,所以本狗才偷梁换柱,改了一下并自用
2.aspose-words-19.1-jdk16.jar 如果采用官网的maven依赖,可能需要自助破解或交费使用

成果展示

请添加图片描述
我是JavaDog,谢谢博友耐心看完, 抽空来我狗窝?瞅瞅呗 blog.javadog.net,关注我的微信公众号有惊喜哦╰(°▽°)╯!

发表评论

表情:
评论列表 (有 0 条评论,80人围观)

还没有评论,来说两句吧...

相关阅读

    相关 开箱的SpringBoot模板

    开箱即用的SpringBoot模板 前言 如果你从事的开发岗位是独立开发一个完整的项目的时候,我们需要前期做很多的开发准备,特别是使用比较多的技术栈的时候,我们前期

    相关 go语言开箱

    一、安装并配置环境 Windows 下可以使用 .msi 后缀(在下载列表中可以找到该文件,如go1.4.2.windows-amd64.msi)的安装包来安装。默认情况下