当前位置: 首页 > news >正文

纯前端生成PDF(jsPDF)并下载保存或上传到OSS

前言

        在工作中遇到了一个需求,就是把前端页面生成PDF并保存在本地,因为前端网站可能会展示各种表格,图表信息内容并带有比较鲜艳的色彩样式,如果让后端生产的PDF的话样式可能和前端页面展示的有所差异,所以这个任务就落到了前端的身上。

技术涉及

  • jsPDF
  • html2canvas 

  • ali-oss

代码实现

1、获取DOM结点

        首先需要获取需要打印的DOM结点,这个时候获取的DOM结点是带有样式的,就相当于页面中的内容

 const eleHtml = document.querySelector('.zxksBody');

2、获取打印容器的属性

        首先做个兼容判断,判断是否取到了DOM结点信息,如果取到了DOM结点就获取DOM结点的内容,进行高度和宽度的赋值

 if (eleHtml) {let eleW = eleHtml.offsetWidth; // 获得该容器的宽let eleH = eleHtml.offsetHeight; // 获得该容器的高}

3、生成PDF

        这一步就是把获取到的DOM结点,通过jsPDF和html2canvas 生成为PDF

html2canvas(eleHtml, {dpi: 300,width: eleW,height: eleH,scale: 2, // 提高渲染质量useCORS: true  //允许canvas画布内 可以跨域请求外部链接图片, 允许跨域请求。}).then(async (canvas) => {const pdf = new jsPDF('', 'pt', 'a4');const imgData = canvas.toDataURL('image/png', 1.0);//a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高const imgWidth = 555.28;//一页pdf显示html页面生成的canvas高度;const imgHeight = 555.28 / canvas.width * canvas.height;// 计算分页const pageHeight = 841.89;//未生成pdf的html页面高度let leftHeight = imgHeight;//页面偏移let position = 0;if (leftHeight < pageHeight) {//在pdf.addImage(pageData, 'JPEG', 左,上,宽度,高度)设置在pdf中显示pdf.addImage(imgData, 'PNG', 20, 20, imgWidth, imgHeight);} else { // 分页while (leftHeight > 0) {pdf.addImage(imgData, 'PNG', 20, position, imgWidth, imgHeight);leftHeight -= pageHeight;position -= 841.89;if (leftHeight > 0) {pdf.addPage();}}});

4、保存本地或者上传OSS

 保存本地

        保存本地的话比较简单,直接调用PDF库自带的方法就可以保存到本地

pdf.save(`${state.xsMc}-${state.xsBh}.pdf`)
上传OSS

        上传的OSS的话就比较复杂一点,首先就是需要配置OSS的内容,然后把PDF转换为Blob对象,最后就是调用OSS的接口实现上传。

// 配置OSS
const client = new OSS({region: "******",bucket: 'bucketName',endpoint: 'endpoint',stsToken: 'securityToken',accessKeyId: 'accessKeyId',accessKeySecret: 'accessKeySecret',
});// 将 PDF 文件转换为 Blob 对象
const pdfBlob = pdf.output('blob');// 调用OSS上方实现上传
const fileRes = await client.put(`${state.xsMc}-${state.xsBh}.pdf`, pdfBlob);
console.log(fileRes, '接收返回的OSS信息');

5、注意事项

  •  使用html2canvas和jsPDF可能会遇见文本错位或者样式错误问题,这个时候需要进行调整,可以通过html2canvas中的onclone回调方法进行调整
html2canvas(eleHtml, {onclone: (documentClone) => {// 在克隆的文档上进行修改const partRight2 = documentClone.querySelector('.partRight2');const titleBars = documentClone.querySelectorAll('.titleBar');if (partRight2) {partRight2.style.display = 'none'; // 隐藏内容}if (titleBars) {//修改样式属性titleBars.forEach(titleBar => {titleBar.style.marginTop = '-8px';titleBar.style.marginBottom = '20px';});}},dpi: 300,width: eleW,height: eleH,scale: 2, // 提高渲染质量useCORS: true  //允许canvas画布内 可以跨域请求外部链接图片, 允许跨域请求。
}).then(async (canvas) => {.......});
  • 对于在获取DOM时,带有滚动条的内容无法正确获取他的高度和宽度,内容可能会被遮盖无法正确打印,这个时候需要在打印前更改页面中的DOM样式才能正确打印
// 获取全部内容
const eleHtml = document.querySelector('.zxksBody');// 在生成canvas之前就把样式进行更改,获取盒子的正常高度或者宽度,防止样式被遮盖,
const changeHeight = document.querySelector('.zxksContent');if (changeHeight) {changeHeight.style.height = '100%'; // 更改高度
}html2canvas(eleHtml, {dpi: 300,width: eleW,height: eleH,scale: 2, // 提高渲染质量useCORS: true  //允许canvas画布内 }).then(async (canvas) => {.....// 在打印完成后,再把样式改回去if (changeHeight) {changeHeight.style.height = 'calc(100vh - 182px)';}}
  • 对于带有滚动条的div盒子,在点击打印时,最好把页面内容进行更改,防止无法正确获取盒子高度,导致文字被隐藏,在打印完成后,在更改回去

// 对于vue

可以使用v-if进行更换,把展示的内容保存在div中,去掉溢出滚动功能

// 对于react

可以使用三元运算符进行判断,展示的内容

6、完整代码

const printPdf = async () => {const client = new OSS({const client = new OSS({region: "******",bucket: 'bucketName',endpoint: 'endpoint',stsToken: 'securityToken',accessKeyId: 'accessKeyId',accessKeySecret: 'accessKeySecret',}); try {// 获取全部内容const eleHtml = document.querySelector('.zxksBody');// 带有移除隐藏的功能const changeHeight = document.querySelector('.zxksContent');if (changeHeight) {changeHeight.style.height = '100%'; // 更改高度}if (eleHtml) {let eleW = eleHtml.offsetWidth; // 获得该容器的宽let eleH = eleHtml.offsetHeight; // 获得该容器的高// 确保获取加载完全的DOMsetTimeout(() => { html2canvas(eleHtml, {onclone: (documentClone) => {// 在克隆的文档上进行修改const partRight2 = documentClone.querySelector('.partRight2');const titleBars = documentClone.querySelectorAll('.titleBar');if (partRight2) {partRight2.style.display = 'none'; // 隐藏内容}if (titleBars) {titleBars.forEach(titleBar => {titleBar.style.marginTop = '-8px';titleBar.style.marginBottom = '20px';});}},dpi: 300,width: eleW,height: eleH,scale: 2, // 提高渲染质量useCORS: true  //允许canvas画布内 可以跨域请求外部链接图片, 允许跨域请求。}).then(async (canvas) => {const pdf = new jsPDF('', 'pt', 'a4');const imgData = canvas.toDataURL('image/png', 1.0);const imgWidth = 555.28;const imgHeight = 555.28 / canvas.width * canvas.height;// 计算分页const pageHeight = 841.89;let leftHeight = imgHeight;let position = 0;if (leftHeight < pageHeight) {pdf.addImage(imgData, 'PNG', 20, 20, imgWidth, imgHeight);} else {while (leftHeight > 0) {pdf.addImage(imgData, 'PNG', 20, position, imgWidth, imgHeight);leftHeight -= pageHeight;position -= 841.89;if (leftHeight > 0) {pdf.addPage();}}}// 将 PDF 文件转换为 Blob 对象const pdfBlob = pdf.output('blob');// 使用 OSS 客户端上传 Blob 对象try {const fileRes = await client.put(`${state.xsMc}-${statexsBh}.pdf`, pdfBlob);console.log('client res', fileRes);} catch (err) {console.error('PDF上传失败,请重新提交!', err);}if (changeHeight) {changeHeight.style.height = 'calc(100vh - 182px)';}});}, 1000);}} catch (error) {console.log("Error!", error);if (changeHeight) {changeHeight.style.height = 'calc(100vh - 182px)';}}};


http://www.mrgr.cn/news/64454.html

相关文章:

  • 前端与后端,利用对象引用优雅地创建树形(Tree)结构数据
  • TS 项目中给常用的路径定义一个别名 tsconfig.json
  • 【Oracle APEX开发小技巧10】CSS样式控制交互式报表列宽和自动换行效果
  • 第三章数据链路层-(3.4流量控制与可靠传输机制)
  • TCP/IP协议介绍
  • 【skywalking 】More than 15,000 ‘grammar‘ tokens have been presented. 【未解决请求答案】
  • CSS中的 BFC,是啥呀?
  • 无源元器件-磁珠选型参数总结
  • 32单片机HAL库的引脚初始化
  • C语言第11节:指针(1)
  • 05 Django 框架模型介绍(一)
  • 虚拟机安装Ubuntu系统
  • 网络请求优化:理论与实践
  • 【Python项目管理】“无法创建虚拟环境”报错原因及解决方法
  • JZ2440开发板——LCD
  • 什么是软件测试?软件测试的流程?软件测试未来3-5年的职业规划?
  • 【AD】1-2 AD24软件的中英文版本切换
  • Python数据分析案例62——基于MAGU-LSTM的时间序列预测(记忆增强门控单元)
  • 不同网线类型
  • 数据库->联合查询
  • Ubuntu使用Qt虚拟键盘,支持中英文切换
  • 网鼎杯-re2-好久不见5
  • C语言 ——— 学习和使用 strstr 函数,并模拟实现
  • [Redis] Redis事务
  • 高频电子线路---一文读懂调幅
  • Ubuntu - 进入紧急模式,无法进入桌面