@记录PDF开发过程
pdf导出功能开发方案
公司监控新的需求需要导出pdf功能,现在平台暂时不支持,后端不愿意代码写pdf,所以只好找点工具用用。
1. 方案一:使用html转换pdf的方法
1.1. 流程:
具体流程如图所示,忽略巡检触发的过程,生成PDF的流程可以总结为:go-gin模块渲染html,通过转换工具将html转为pdf。
1.2. 转换工具:
转换工具有很多,phatomjs、wkhtmltopdf和pd4ml等等。
- phatomjs不再维护,而且页面大小不好设置;
- wkhtmltopdf命令多,坑也多,对应静态模版还阔以;
- pd4ml得api好用,但是有水印。
综上所述,优先选择wkhtmltopdf,具体依赖见文件夹。
附(转换命令)
## 1.转换命令
wkhtmltopdf https://www.csdn.net /Users/kevin/Desktop/csdn.pdf
这些工具都有通病,不能转换动态渲染的页面,类似react框架等等,这就产生一个问题,生成所需html模版应该如何摆放。
1.3. html模版的摆放问题
这里也是有两种方法:
- 需要支持html,直接放在文件夹下,通过部署脚本将静态文件移动到指定位置,但是css等内容经常改动,这样脚本变动太多;
- 转成bindata,放在监控模块中,这样可以直接通过内部网络访问,还不会造成安全问题。
两种方案中,最好都把对html的支持方在监控服务中,因为监控服务也支持了gin,不需要添加太多的依赖。
附(生成bindata方法):
## 1.添加依赖
go get github.com/jteeuwen/go-bindata/...
go get github.com/elazarl/go-bindata-assetfs/...
## 2.转换文件
>go-bindata-assetfs -o ./modules/api/app/static/static.go -pkg=static ./modules/api/app/static/...
1.4. html模版
需要简单写一写的那种,css和js可能要前端支援,瞎逼写个页面,转换出来,长下面这样:
2. 方案二:前端添加pdf导出功能
2.1 现成功能
参考链接:https://www.cnblogs.com/alannxu/p/10982952.html
试了下,是能用得,可是前端又不同意,我也是难得。
<template>
<div class="pdf-preview-container">
<div
class="page-container"
ref="container"
v-for="page in docPages"
:style="{
height: `${pageHeight}px`
}"
:key="page" v-show='isLoaded'>
<canvas v-if="renderList.includes(page)">
</canvas>
</div>
<div class="notice" v-show="!isLoaded">
<h4>加载中...</h4>
</div>
</div>
</template>
<script>
// import pdfJS from 'pdfjs-dist'
export default {
props: {
url: {
type: String,
required: true
},
renderPages: {
type: Number,
default: 5
},
customScroll: {
type: Boolean,
default: false
},
offsetHeight: {
type: Number,
default: 0
}
},
data() {
return {
doc: null,
docPages: 0,
currentPage: 0,
pageHeight: 0,
renderList: [],
show: false,
notice: '',
timer: 0,
isLoaded:true
};
},
watch: {
url: {
immediate: true,
handler() {
this.getPDFFile();
}
}
},
created() {
if (!this.customScroll) {
document.addEventListener('scroll', this.scroll);
}
},
beforeDestroy() {
document.removeEventListener('scroll', this.scroll);
},
methods: {
getPDFFile() {
if (!this.url) return;
this.currentPage = 0;
// pdfJS.getDocument(this.url).then(pdf => {
// this.isLoaded = true;
// this.doc = pdf
// this.docPages = pdf.numPages
// this.$nextTick(() => {
// this.docPages && this.scrollToPage(1)
// })
// })
},
scrollToPage(pageNo) {
if (this.currentPage === pageNo) return;
this.currentPage = pageNo;
let list = [];
for (let page = pageNo - this.renderPages; page <= pageNo + this.renderPages; page++) {
list.push(page);
}
list = list.filter(page => page <= this.docPages && page >= 1);
this.$nextTick(() => {
this.renderList = list;
this.renderList.forEach(page => {
this.renderPage(page);
});
});
},
// 渲染page
renderPage(pageNo) {
this.doc.getPage(pageNo).then(page => {
let container = this.$refs.container[pageNo - 1];
if (!container) return;
let canvas = container.querySelector('canvas');
if (!canvas || canvas.__rendered) return;
let ctx = canvas.getContext('2d');
let dpr = window.devicePixelRatio || 1;
let bsr = ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1;
let ratio = dpr / bsr;
let rect = container.getBoundingClientRect();
let viewport = page.getViewport(1);
let width = rect.width;
let height = width / viewport.width * viewport.height;
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
this.pageHeight = height;
canvas.height = height * ratio;
canvas.width = width * ratio;
ctx.setTransform(ratio, 0, 0, ratio, 0, 0);
page.render({
canvasContext: ctx,
viewport: page.getViewport(width/viewport.width)
});
canvas.__rendered = true;
});
},
scroll() {
this.checkRender(document.documentElement);
},
checkRender(el) {
if (!this.pageHeight) return;
let scrollTop = el.scrollTop;
if (el === document.documentElement) {
scrollTop = el.scrollTop || window.pageYOffset || document.body.scrollTop;
}
let page = Math.floor((scrollTop - this.offsetHeight) / this.pageHeight);
page = Math.max(page, 1);
page = Math.min(page, this.docPages);
this.scrollToPage(page);
}
}
};
</script>
<style lang="scss" scoped>
.notice{
position: fixed;
background:#000;
color: #fff;
left: 2rem;
top: 5rem;
right: 2rem;
}
</style>
后记,最后还是选了方案一。。。。。