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

news2024/11/7 9:27:47

前言

        在工作中遇到了一个需求,就是把前端页面生成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; // 获得该容器的高

            // 确保获取加载完全的DOM
            setTimeout(() => { 
              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.coloradmin.cn/o/2234921.html

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

相关文章

云渲染与汽车CGI图像技术优势和劣势

在数字时代&#xff0c;云渲染技术以其独特的优势在汽车CGI图像制作中占据了重要地位。云渲染通过利用云计算的分布式处理能力&#xff0c;将渲染任务分配给云端的服务器集群进行计算&#xff0c;从而实现高效、高质量的渲染效果。 这种技术的优势主要体现在以下几个方面&#…

鸿蒙NEXT开发-学生管理系统小案例(基于最新api12稳定版)

注意&#xff1a;博主有个鸿蒙专栏&#xff0c;里面从上到下有关于鸿蒙next的教学文档&#xff0c;大家感兴趣可以学习下 如果大家觉得博主文章写的好的话&#xff0c;可以点下关注&#xff0c;博主会一直更新鸿蒙next相关知识 专栏地址: https://blog.csdn.net/qq_56760790/…

《潜行者2切尔诺贝利之心》游戏引擎介绍

潜行者2切尔诺贝利之心是基于虚幻5引擎&#xff0c;所以画面效果大家不必担心。游戏目前已经跳票了很久&#xff0c;预计发售时间是2024 年 11 月 21 日&#xff0c;这次应该不会再跳票。 潜行者2切尔诺贝利之心是虚幻5吗 答&#xff1a;是虚幻5。 潜行者官方推特之前回复了…

WPF+MVVM案例实战(十八)- 自定义字体图标按钮的封装与实现(ABD类)

文章目录 1、案例效果1、按钮分类2、ABD类按钮实现描述1.文件创建与代码实现2、样式引用与控件封装3、按钮案例演示1、页面实现与文件创建2、运行效果如下3、总结4、源代码获取1、案例效果 1、按钮分类 在WPF开发中,最常见的就是按钮的使用,这里我们总结以下大概的按钮种类,…

在Vue和OpenLayers中使用移动传感器实现飞机航线飞行模拟

项目实现的核心代码 项目概述 该项目的目标是使用Vue.js作为前端框架&#xff0c;结合OpenLayers用于地图显示&#xff0c;实时获取来自手机传感器的数据&#xff08;如经纬度、高度、速度&#xff09;来模拟飞机在地图上的飞行轨迹。整体架构如下&#xff1a; Vue.js 用于构建…

C语言-详细讲解-洛谷P1075 [NOIP2012 普及组] 质因数分解

1.题目要求 2.题目解析 解题点在于如何分解质因数&#xff0c;这里介绍一下短除法。&#xff08;虽然解决这个问题可以不用短除法&#xff09; 3.代码实现 贴一下自己的代码 #include <stdio.h> #include <math.h>int main() {int n, i;scanf("%d",…

基于springboot的音乐网站的设计与实现(源码+lw+调试)

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下&#xff0c;你想解决的问…

RabbitMQ 管理平台(控制中心)的介绍

文章目录 一、RabbitMQ 管理平台整体介绍二、Overview 总览三、Connections 连接四、Channels 通道五、Exchanges 交换机六、Queues 队列查看队列详细信息查看队列的消息内容 七、Admin 用户给用户分配虚拟主机 一、RabbitMQ 管理平台整体介绍 RabbitMQ 管理平台内有六个模块&…

Golang | Leetcode Golang题解之第542题01矩阵

题目&#xff1a; 题解&#xff1a; type point struct{x, y int }var dirs []point{{-1, 0}, {1, 0}, {0, -1}, {0, 1}}func updateMatrix(mat [][]int) [][]int {var m, n len(mat), len(mat[0])var res make([][]int, m)var visited make([][]bool, m)var queue []poin…

前端介绍|基础入门-html+css+js

文章目录 本课程有什么&#xff1f;前端是什么&#xff1f;1. **前端概述**2. **前端的工作职责**3. **前端技术栈**6. **前端开发工具**7. **HTML、CSS、JS的关系** 本课程有什么&#xff1f; 本套课程是零基础入门保姆级课程&#xff0c;课程主要内容包含&#xff1a; HTML…

自动驾驶---“火热的”时空联合规划

1 背景 早期的不少规划算法都是横纵分离的&#xff08;比如Apollo&#xff09;&#xff0c;先求解path之后&#xff0c;依赖path的结果再进行speed的求解。这种横纵解耦的规划方式具有以下特点&#xff1a; 相对较为简单&#xff0c;计算量通常较小&#xff0c;容易实现实时性…

龙蜥副理事长张东:加速推进 AI+OS 深度融合,打造最 AI 的服务器操作系统

AI 原生时代&#xff0c;操作系统厂商要全面优先拥抱 AI&#xff0c;深度融合 AI 能力&#xff0c;发挥关键生态位作用&#xff0c;做好上游芯片与下游 AI 应用开发商之间的纽带&#xff0c;打造最 AI 的服务器操作系统&#xff0c;实现 AI 能力的快速价值转化。 AI 原生趋势下…

详解Java之Spring MVC篇二

目录 获取Cookie/Session 理解Cookie 理解Session Cookie和Session的区别 获取Cookie 获取Session 获取Header 获取User-Agent 获取Cookie/Session 理解Cookie HTTP协议自身是“无状态”协议&#xff0c;但是在实际开发中&#xff0c;我们很多时候是需要知道请求之间的…

【金融风控】相关业务介绍及代码详解

金融风控相关业务介绍 【了解】项目整体介绍 1.风控业务和风控报表</span> 零售金融产品 相关的指标 风控建模流程 ​ #2.特征工程 特征构造 特征筛选 ​ 3.评分卡模型构建 逻辑回归 集成学习 XGBoost LightGBM 模型评估 ​ #4.样本不均衡问题/异常点检测 【了解】今日…

了解数据库设计中的反规范化

反规范化是指通过增加冗余数据来提高数据库的读取效率。也就是说,反规范化通过在表中增加冗余字段来减少数据库中的表连接,以提高查询速度。规范化和反规范化是关系型数据库设计中的两个重要方面,它们分别代表了数据组织方式上的两个不同方向。规范化是为了减少数据冗余和提…

Perforce《2024游戏技术现状报告》Part2:游戏引擎、版本控制、IDE及项目管理等多种开发工具的应用分析

游戏开发者一直处于创新前沿。他们的实践、工具和技术受到各行各业的广泛关注&#xff0c;正在改变着组织进行数字创作的方式。 近期&#xff0c;Perforce发布了《2024游戏技术现状报告》&#xff0c;通过收集来自游戏、媒体与娱乐、汽车和制造业等高增长行业的从业者、管理人…

JAVA基础:数组 (习题笔记)

一&#xff0c;编码题 1&#xff0c;数组查找操作&#xff1a;定义一个长度为10 的一维字符串数组&#xff0c;在每一个元素存放一个单词&#xff1b;然后运行时从命令行输入一个单词&#xff0c;程序判断数组是否包含有这个单词&#xff0c;包含这个单词就打印出“Yes”&…

通过 SSH 连接远程 Ubuntu 服务器

目录 安装 SSH 服务器允许 SSH 通过防火墙远程 SSH 连接&#xff08;选&#xff09;重启向日葵 安装 SSH 服务器 更新软件包列表 sudo apt update安装 OpenSSH 服务器 sudo apt install openssh-server检查 SSH 服务器状态 sudo systemctl status ssh如果 SSH 服务器正在运…

http请求响应详解

http介绍 http协议&#xff1a; Http”协议称为是“超文本传输协议”&#xff08;HTTP-Hypertext transfer protocol&#xff09;。它定义了浏览器怎么向万维网服务器请求万维网文档&#xff0c;以及服务器怎么样把文档传送给浏览器。 https协议&#xff1a; 传统的HTTP协议…

使用 OpenCV 实现图像的透视变换

概述 在计算机视觉领域&#xff0c;经常需要对图像进行各种几何变换&#xff0c;如旋转、缩放和平移等。其中&#xff0c;透视变换&#xff08;Perspective Transformation&#xff09;是一种非常重要的变换方式&#xff0c;它能够模拟三维空间中的视角变化&#xff0c;例如从…