纯前端生成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.xdnf.cn/news/4421.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

怎样使用pycharm的服务?

‌在PyCharm中使用服务器的步骤如下‌&#xff1a; ‌打开PyCharm&#xff0c;选择“File”->“Settings”‌。‌选择左侧工具栏中的“Project:…”‌&#xff0c;然后选择“Python Interpreter”。‌点击右上角的“Add Interpreter”‌&#xff0c;选择“On SSH”。‌如果…

CI_CD

什么是CI/CD 在前端开发中&#xff0c;CI/CD 是 Continuous Integration&#xff08;持续集成&#xff09;和 Continuous Deployment/Continuous Delivery&#xff08;持续部署/持续交付&#xff09;的简称。它是一种软件开发实践&#xff0c;自动化了应用的构建、测试和发布过…

设置JAVA以适配华为2288HV2服务器的KVM控制台

华为2288HV2服务器比较老旧了&#xff0c;其管理控制台登录java配置比较麻烦&#xff0c;华为的ibmc_kvm_client_windows客户端测试了几个版本&#xff0c;连接控制台也有问题&#xff0c;最终安装JDK解决。 一、测试环境 主机为WindowsServer2012R2,64位系统 二、Java软件包…

机器学习—构建一个神经网络

如何在Tensorflow中构建神经网络&#xff1f; 回到之前的例子&#xff0c;如果你想做钱进支柱&#xff0c;初始化数据x创建第一层&#xff0c;如下图所示计算一个1&#xff0c;然后创建第二层并计算一个2&#xff0c;所以这是一种明确的向前推进的方式。 事实证明&#xff0c;…

基于51单片机的步进电机定时控制proteus仿真

地址&#xff1a; https://pan.baidu.com/s/1_F5bfyS_e_eKSblnja7RqA 提取码&#xff1a;1234 仿真图&#xff1a; 芯片/模块的特点&#xff1a; AT89C52/AT89C51简介&#xff1a; AT89C52/AT89C51是一款经典的8位单片机&#xff0c;是意法半导体&#xff08;STMicroelectro…

【云原生开发】如何通过client-go来操作K8S集群

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

idea 配置自动导入设置

配置自动导入设置&#xff1a; 通过访问 Settings→Editor→General→Auto Import&#xff0c;勾选 Add unambiguous imports on the fly 和 Optimize imports on the fly 选项&#xff0c;可以实现自动添加明确的导入和快速优化导入&#xff0c;即自动删除无用的导入

【文本情感分析识别】Python+SVM算法+模型训练+文本分类+文本情感分析

一、介绍 使用Python作为开发语言&#xff0c;基于文本数据集&#xff08;一个积极的xls文本格式和一个消极的xls文本格式文件&#xff09;&#xff0c;使用Word2vec对文本进行处理。通过支持向量机SVM算法训练情绪分类模型。实现对文本消极情感和文本积极情感的识别。并基于D…

iptables面试题

1、详述iptales工作流程以及规则过滤顺序&#xff1f; iptables过滤的规则顺序是由上至下&#xff0c;若出现相同的匹配规则则遵循由上至下的顺序 2、iptables的几个表以及每个表对应链的作用&#xff1f; Iptables有四表五链 Filter表 : Filter表是iptables中使用的默认表…

【青牛科技】GC2803:白色家电与安防领域中 ULN2803 的卓越替代者

在当今科技飞速发展的时代&#xff0c;电子元器件在各个领域都扮演着至关重要的角色。在白色家电和安防等产品的电路设计中&#xff0c;驱动芯片的选择尤为关键。传统的 ULN2803 曾是广泛应用的一款芯片&#xff0c;但如今&#xff0c;芯麦 GC2803 的出现为这些领域带来了新的选…

变脸、看完毒液3,我把自己的脸变毒液了,视频有点惊悚!请谨慎观看

大家好&#xff0c;我是Shelly&#xff0c;一个专注于输出AI工具和科技前沿内容的AI应用教练&#xff0c;体验过300款以上的AI应用工具。关注科技及大模型领域对社会的影响10年。关注我一起驾驭AI工具&#xff0c;拥抱AI时代的到来。 AI工具集1&#xff1a;大厂AI工具【共23款…

数据分析:微生物功能差异分析之Maaslin2

文章目录 介绍加载R包数据链接导入数据数据预处理Maaslin2检验输出结果画图系统信息介绍 Maaslin2(Microbiome Multivariable Associations with Linear Models)是一种用于微生物组学数据的多变量关联分析工具。它的原理和特点如下: 多变量关联分析: Maaslin2旨在高效确定…

电脑软件:推荐四款非常好用的电脑磁盘分析工具

一、WizTree WizTree 是一款Windows下磁盘空间分析工具。它可以快速扫描并分析你的电脑硬盘驱动器中文件和文件夹&#xff0c;并以可视化块状的方式展示哪些文件和文件夹使用的磁盘空间最多。这样你就可以很方便找到占用磁盘空间大的文件。 WizTree的特点 ● 磁盘空间利…

6. STM32之TIM实验--编码器接口()--(实验5:PWM驱动直流电机)

这篇文章是通用定时器的最后一章节&#xff0c;也就是编码器接口&#xff0c;主要是用来进行对精确测量旋转角度或速度的负载进行精确控制。 STM32 编码器模式详解-CSDN博客 STM32——编码器测速原理及STM32编码器模式_龙邱512编码器stm32历程-CSDN博客 代码可根据这个进行编…

如何批量创建文件夹并命名?6个一键批量创建的方法

如何批量创建文件夹并命名&#xff1f;在快节奏的现代工作环境中&#xff0c;时间成为了最宝贵的资源。面对海量的数据与文件&#xff0c;如何高效地组织与管理&#xff0c;成为了提升工作效率的关键。为了节省时间&#xff0c;批量创建文件夹并命名&#xff0c;成为了一项至关…

人工智能之人脸识别(face_recognition)

文章目录 face_recognition 介绍主要功能**与opencv联系联系检测人脸切割人脸提取人物关键特征计算人脸的欧几里得距离计算人脸匹配程度总结 face_recognition 介绍 face_recognition 介绍 face_recognition 是一个非常流行的 Python 库&#xff0c;专门用于人脸识别任务。它基…

Java学习路线:JUL日志系统(二)使用Properties配置文件

目录 认识properties 使用properties编写日志配置文件 认识properties 之前的学习中&#xff0c;我们学习了使用XML配置文件&#xff0c;但是XML的读取实在有些麻烦。那有没有更简单的方式来配置文件呢&#xff1f; 答案是&#xff1a;使用Properties配置文件 在这里了解pro…

【数据库】elasticsearch

1、架构 es会为每个索引创建一定数量的主分片和副本分片。 分片&#xff08;Shard&#xff09;&#xff1a; 将索引数据分割成多个部分&#xff0c;每个部分都是一个独立的索引。 主要目的是实现数据的分布式存储和并行处理&#xff0c;从而提高系统的扩展性和性能。 在创建索…

Qt——常用控件

前言&#xff1a;本篇文章&#xff0c;将分享Qt中常用的&#xff0c;具有代表性的一些控件。 一.按钮类控件 在前边的文章中我们也多次分享过PushButton按钮&#xff0c;但Qt中并非只提供这一种按钮。 在Qt中&#xff0c;QPushButton并非直接继承自QWidget&#xff0c;QAbstr…

硬件基础06 滤波器——无源、有源(含Filter Solutions、Filter Pro、MATLAB Fdatool)

推荐设计RC无源滤波器使用&#xff0c;数字滤波器可以使用MATLAB&#xff0c;有源滤波器使用Filter Pro。 一、Filter Solutions 1、软件资源及安装教程如下 FilterSolutions14.1.rar资源 &#xff08;1&#xff09;、双击FS14_1_0.exe进行安装&#xff1b;不要安装到中文路…