需求场景:
web界面已经生成了对应的报告信息模板和相关文件,现需要将网页文档转为pdf下载答应,同时网页中指标内容会应用户的修改而发生变化,现用户需要下载该网页版的报告。
方案一:html2pdf
前端使用html2pdf.js和pdf相关的组件JS,直接将网页转为pdf然后下载。
优点:1、网络上相关参考案例较多,纯前端,不用后台开发费力。
2、转化时间段,效率高,几乎可以实现实时转化,服务器压力小;
缺点:1、对浏览器的兼容要求比较高,有些页面语法无法实现,浏览器分辨率对生成的文件清晰度影响较大,虽然可以通过设置像素处理,但是比较繁琐;按照官网案例很难实现清晰度较高的文档,且坑较多,笔者为后台开发选手,处理很难,放弃。
遇到的问题:
1、html2pdf使用canvas对页面进行截屏,然后转化为pdf,对网页中原有的echarts绘制的图像转canvas元素时出现空白,对跨域的网页图片转化也会出现问题;
相关案例可以在gitee或网页中搜索,有很多案例。
通过使用和测试,感觉改该插比较适用网页简单,对内容要求较低,实时性强的场景。
方案二:poi-tl
可参考:
且没有可用的word文档模板(文档模板可以自己定义修改,但工作量大),所以放弃了使用java poi 在服务端转化的方案。
官网文档较详细,生成word后转pdf很容易。
个人人为适用于文档内容格式固定,内容丰富但变化较小的场景。
方案三:frammark
该方案在网上查过,但是要后台熟悉使用,有点类似poi,考虑开发周期,放弃。
方案四:nodejs文档转化服务
选用该方案是考虑到项目中有很多网页转化场景,使用单一服务比较好扩展。
安装nodejs过程如下:
1、下载安装nodejs对应版本,配置bin目录至环境变量 ,linux同样
参考地址 : https://www.runoob.com/nodejs/nodejs-install-setup.html
2、cnpm
npm install -g cnpm --registry=https://registry.npm.taobao.org
3、 安装结果验证:
版本验证,命令为:node -v和npm -v
查看位置:where node
查看淘宝镜像设置,命令为:npm get registry
查看npm全局路径设置情况,命令为:npm install webpack -g
4、执行命令:npm init 初始化生成package.json (执行命令回车即可)
5、 项目中安装mysql,执行命令为:
npm install express mysql --save
6、 项目中安装puppeteer,执行命令为:
cnpm install puppeteer --save
cnpm install bufferutil --save
cnpm install utf-8-validate --save
7、启动服务 node app.js
linux 中以上命令也可执行。特别的,linux环境下生成的pdf可能会有部分中文乱码,出现方框,所以需要修改linux 字体配置等。
案例代码如下:
// 引入相关库文件
//var express = require('express');
const puppeteer = require('puppeteer');
const config = require('./config.js');
const mysqlUtils = require('./MysqlUtils.js');
//var app = express();
let intervalObj = null;
/**
* 定义node服务对象
**/
var nodeServerObj = {
/**
* 开启服务
*/
startServer: function () {
try {
mysqlUtils.start();
console.log("node server start---------------------");
// 定时获取任务
this.getUnstartDatas();
intervalObj = setInterval(nodeServerObj.getUnstartDatas, config.INTERVAL_TIME);
} catch (e) {
console.error(e);
}
},
/**
* 停止服务
*/
stopServer: function () {
if (intervalObj) {
clearInterval(intervalObj);
intervalObj = null;
}
},
/**
* 获取未开始的数据内容
*/
getUnstartDatas: function () {
if (config.ISSHOW_DEBUGINFO) {
console.log("getUnstartDatas---------------------");
}
let sql = "select id, semester, class_id, child_id, report_file_name, report_file_save_name, status from rk_lb_dg_report_prisefile where status='0' order by id";
mysqlUtils.executeSql(sql, function (err, result) {
if (err) {
console.error(err);
} else {
for (let i in result) {
nodeServerObj.startWork(result[i]);
}
}
});
},
/**
* 开始下载文件(先修改状态再进行转换)
* @param {*} reportItem
*/
startWork: function (reportItem) {
if (config.ISSHOW_DEBUGINFO) {
console.log("startWork---------------------");
console.log("操作文件:" + JSON.stringify(reportItem));
}
let sql = "update rk_lb_dg_report_prisefile set status='2', modify_time=now() where id=" + reportItem['id'];
mysqlUtils.executeSql(sql, function (err, result) {
if (err) {
console.error(err);
} else {
nodeServerObj.createReportFile(reportItem);
}
});
},
/**
* 生成报告文件(通过puppeteer进行转换)
* @param {*} reportItem
*/
createReportFile: function (reportItem) {
let url = config.REPORT_SERVER_PREVIEW_URL + "?childId=" + reportItem['child_id'] + "&semester=" + reportItem['semester'] + "&classId=" + reportItem['class_id'];
if (config.ISSHOW_DEBUGINFO) {
console.log("createReportFile---------------------url="+url);
}
var saveFile = config.DOWNLOAD_DIRECTORY + reportItem['report_file_save_name'];
(async () => {
try {
const browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox']
})
let loadUiFlag = true;// 加载页面是否正常,默认为true
const page = await browser.newPage();
page.setUserAgent("Mozilla/5.0 (Linux; Android 8.1.0; MI 8 Build/OPM1.171019.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/62.0.3202.84 Mobile Safari/537.36");
// page.on('response',(res)=>{
// let url = res.url();
// if(res.status() == 404){
// console.error("页面访问异常,url="+url);
// loadUiFlag = false;
// // 转换失败
// nodeServerObj.stopWork(reportItem['id'], 1, saveFile);
// }/*else{
// console.log("监听事件中正常访问的地址信息,url="+url+", status: "+res.status());
// }*/
// });
await page.goto(url, { waitUntil: 'networkidle0' });
await page.pdf({
path: saveFile,
format: 'A4'
});
await browser.close();
// 转换成功
if(loadUiFlag){
nodeServerObj.stopWork(reportItem['id'], 3, saveFile);
}
} catch (e) {
console.error(e);
// 转换失败
nodeServerObj.stopWork(reportItem['id'], 1, saveFile);
}
})();
},
/**
* 结束转换文件工作
* @param {*} id
* @param {*} status 转换结果,1:转换失败,3:转换完成
*/
stopWork: function (id, status, saveFile) {
if (config.ISSHOW_DEBUGINFO) {
console.log("stopWork---------------------");
}
let sql = "update rk_lb_dg_report_prisefile set status='" + status + "', modify_time=now() where id=" + id;
mysqlUtils.executeSql(sql, function (err, result) {
if (err) {
console.error(err);
}else{
console.log("id: " + id + ", status: " + status + ", saveFile: " + saveFile);
}
});
}
};
// 初始化服务
nodeServerObj.startServer();