纯前端生成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.mzph.cn/pingmian/59340.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

多商户电商平台开发指南:基于直播带货系统源码的搭建方案详解

本篇文章&#xff0c;小编将详细解析如何利用直播带货系统源码&#xff0c;快速搭建一套多商户电商平台的解决方案。 一、直播带货系统在多商户电商平台中的应用价值 在多商户电商平台中&#xff0c;直播带货系统可以帮助商家&#xff1a; 1.增加用户互动 2.提升转化率 3.…

登录功能设计(php+mysql)

一 登录功能 1. 创建一个登录页面&#xff08;login.php&#xff09;&#xff0c;包含一个表单&#xff0c;用户输入用户名和密码。 2. 在表单的提交事件中&#xff0c;使用PHP代码处理用户输入的用户名和密码。 3. 首先&#xff0c;连接MySQL数据库。然后&a…

P3-1.【结构化程序设计】第一节——知识要点:算法、顺序结构程序设计、if语句的语法结构及各种用法

讲解视频&#xff1a; P3-1.【结构化程序设计】第一节——知识要点&#xff1a;算法、顺序结构程序设计、if语句的语法结构及各种用法 知识要点&#xff1a;算法、顺序结构程序设计、if语句的语法结构及各种用法 一、算法、顺序结构程序设计任务分析 知识要点&#xff1a;算法…

18、论文阅读:AOD-Net:一体化除雾网络

AOD-Net: All-in-One Dehazing Network 前言介绍相关工作物理模型传统方法深度学习方法 建模与扩展变换后的公式网络设计与高级特征任务相结合 除雾评价数据集和实现 前言 该论文提出了一种基于卷积神经网络&#xff08;CNN&#xff09;的图像去雾模型&#xff0c;称为 All-in…

Golang | Leetcode Golang题解之第538题把二叉搜索树转换为累加树

题目&#xff1a; 题解&#xff1a; func getSuccessor(node *TreeNode) *TreeNode {succ : node.Rightfor succ.Left ! nil && succ.Left ! node {succ succ.Left}return succ }func convertBST(root *TreeNode) *TreeNode {sum : 0node : rootfor node ! nil {if n…

docker+nacos

安装数据库 以docker安装为例&#xff08;实际建议实体&#xff09; 初始化数据库 /******************************************/ /* 数据库全名 nacos_config */ /* 表名称 config_info */ /******************************************/ CREATE TABLE config_i…

react18中redux-promise搭配redux-thunk完美简化异步数据操作

用过redux-thunk的应该知道&#xff0c;操作相对繁琐一点&#xff0c;dispatch本只可以出发plain object。redux-thunk让dispatch可以返回一个函数。而redux-promise在此基础上大大简化了操作。 实现效果 关键逻辑代码 store/index.js import { createStore, applyMiddlewar…

汇编语言与接口技术--算术运算程序设计

一、 实验要求 编程实现两个数&#xff1a;#998877H 和 #778899H 的加法运算。编程实现两个数&#xff1a;#998877H 和 #778899H 的减法运算。 二、 实验设计 1.整体思路 无符号角度&#xff1a; &#xff08;1&#xff09;加法 1.初始化&#xff1a;设置两个数 998877H 和…

(蓝桥杯C/C++)——基础算法(上)

目录 一、二分法 1.二分法简介 二分法简介-解题步骤 2.整数二分-简介 整数二分-模板 3.浮点二分-简介 浮点二分-模板 4.二分答案-简介 二分答案-模板​​​​​​​ 二、位运算 1.位运算简介 2.常见的位运算 按位与AND(&) 按位或OR( | ) 按位异或…

1-petalinux 问题记录-根文件系统分区问题

在MPSOC上使用SD第二分区配置根文件系统的时候&#xff0c;需要选择对应的bootargs&#xff0c;但是板子上有emmc和sd两个区域&#xff0c;至于配置哪一种mmcblk0就出现了问题&#xff0c;从vivado中的BlockDesign和MLK XCZU2CG原理图来看的话&#xff0c;我使用的SD卡应该属于…

CSS中的背景色和前景色

目录 1 对比度的计算1.1 亮度计算1.2 对比度比率 2 在线计算对比度 在我们的样式设计中&#xff0c;通常会有背景色和前景色的概念。前景色我们通常用来设置文本的颜色&#xff0c;而背景色通常是文本的所在容器的颜色。比如如果我们把文本放在普通容器里&#xff0c;那普通容器…

全面解析:区块链技术及其应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 全面解析&#xff1a;区块链技术及其应用 全面解析&#xff1a;区块链技术及其应用 全面解析&#xff1a;区块链技术及其应用 区…

[Redis] Redis主从复制模式

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏: &#x1f9ca; Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 &#x1f355; Collection与…

初始JavaEE篇——多线程(8):JUC的组件

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a;JavaEE 目录 Callable接口 ReentrantLock synchronized 与 ReentrantLock的区别 信号量&#xff08;Semaphore&#xff09; CountDown…

Java实现JWT登录认证

文章目录 什么是JWT?为什么需要令牌?如何实现?添加依赖&#xff1a;JwtUtils.java&#xff08;生成、解析Token的工具类&#xff09;jwt配置&#xff1a;登录业务逻辑&#xff1a;其他关联代码&#xff1a;测试&#xff1a; 什么是JWT? JWT&#xff08;Json Web Token&…

Meta AR 眼镜团队前负责人加入 OpenAI;visionOS 2.2 Beta 引入超宽屏投屏模式丨 RTE 开发者日报

开发者朋友们大家好&#xff1a; 这里是 「RTE 开发者日报」 &#xff0c;每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE&#xff08;Real-Time Engagement&#xff09;领域内「有话题的 新闻 」、「有态度的 观点 」、「有意思的数据」、「有思考的 文章 …

如何对LabVIEW软件进行性能评估?

对LabVIEW软件进行性能评估&#xff0c;可以从以下几个方面着手&#xff0c;通过定量与定性分析&#xff0c;全面了解软件在实际应用中的表现。这些评估方法适用于确保LabVIEW程序的运行效率、稳定性和可维护性。 一、响应时间和执行效率 时间戳测量&#xff1a;使用LabVIEW的时…

鸢尾博客项目开源

1.博客介绍 鸢尾博客是一个基于Spring BootVue3 TypeScript ViteJavaFx的客户端和服务器端的博客系统。项目采用前端与后端分离&#xff0c;支持移动端自适应&#xff0c;配有完备的前台和后台管理功能。后端使用Sa-Token进行权限管理,支持动态菜单权限&#xff0c;服务健康…

拾光云影 3.3.0 | 高清秒播电视直播,支持IPV4,几千频道

拾光云影是一款Ipv4通用版电视直播APP&#xff0c;界面熟悉但有所改进&#xff0c;操作布局类似TVbox。新增了功能按钮页&#xff0c;提供更多功能。频道清晰&#xff0c;加载速度快&#xff0c;支持港澳台等特殊频道&#xff0c;大部分频道均可秒播。软件内置了直播接口&#…

【HTML】——VSCode 基本使用入门和常见操作

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 零&#xff1a;HTML开发工具VSCode的使用 1&#xff1a;创建项目 2&#xff1a;创建格式模板&#x…