关于java:屎上最全vuepdfSpringboot与asposewords整合开箱即用

65次阅读

共计 9740 个字符,预计需要花费 25 分钟才能阅读完成。

前言

⏲️本文浏览时长:约 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 包本地依赖或者上传私服

<!-- 本地依赖 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")
    @Slf4j
    public 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 = false
import axios from 'axios'
Vue.prototype.$http = axios
import 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
CSDN https://blog.csdn.net/baidu_25986059
掘金 https://juejin.cn/user/2172290706716775
知乎 https://www.zhihu.com/people/JavaDog
简书 https://www.jianshu.com/u/1ff9c6bdb916
gitee https://gitee.com/javadog-net
GitHub https://github.com/javadog-net

正文完
 0