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

PDF 生成(5)— 内容页支持由多页面组成

当学习成为了习惯,知识也就变成了常识。 感谢各位的 关注点赞收藏评论

新视频和文章会第一时间在微信公众号发送,欢迎关注:李永宁lyn

文章已收录到 github 仓库 liyongning/blog,欢迎 Watch 和 Star。

回顾

在本篇开始之前,我们先来回顾一下上篇 PDF 生成(4)— 目录页 的内容:

  • 开头,我们通过 page.evaluate 方法为浏览器注入 JS 代码,通过这段 JS 在 PDF 内容页的开始位置(body 的第一个子元素)插入由 a 标签和对应的样式组成的目录页 DOM,从而通过 HTML 锚点实现目录项的页面跳转能力
  • 接下来,我们通过为目录页的容器元素设置break-after: page样式实现目录页自成一页的效果(和内容页分别两页)
  • 然后剩下的所有篇幅都是在讲如何生成带有准确页码的目录项
    • 首先,页码是按照锚点元素在页面中的高度 / PDF 一页的高度来计算的
    • 后来,我们通过下面三步来保证目录页中目录项对应页码的准确性
      • 规范化设计稿尺寸(按照 A4 纸对应的 2 倍图尺寸设计)
      • 通过页面缩放解决设计稿 DPI 和实际生成 PDF 时 DPI 的差异问题(彻底统一计算时 PDF 一页的像素高度)
      • 通过页面高度补充的方案解决章节标题换页引起目录项页码计算错误的问题

上篇结束之后,PDF 文件的整体框架已完全成型,包括封面、目录页、内容页和尾页四部分。但系列还没结束,接下来我们会通过本文来提升接入方的使用体验和前端代码可维护性。

开始之前,上篇给大家留了一个问题:回顾一下现在 PDF 文件内容页的生成,站在接入方的角度看,是否存在问题?假设一个场景,接入方的 PDF 文件呈现的内容量非常大,比如拥有几十甚至几百页的内容,那接入方的这个前端页面的代码该怎么维护?页面性能又该怎么保证呢?

简介

本系列的 PDF 生成(1)— 开篇、PDF 生成(2)— 生成 PDF 文件、PDF 生成(3)— 封面、尾页、PDF 生成(4)— 目录页 都是在一步步完善 PDF 文件的整体框架,包括封面、目录页、内容页和尾页四部分,截止上篇,PDF 文件的整体框架已完全成型。

本篇是站在用户角度(接入方)来进行的一次技术迭代。目的是为了解决用户前端代码的可维护性问题。

问题

问题 1:到目前为止,我们的 PDF 文件内容页是怎么生成的?

:关键代码之一 await page.goto('https://content.cn', { waitUntil: ['load', 'networkidle0'] }),也就是说,PDF 文件的内部部分都是由该链接背后的前端页面提供的。


问题 2:如果一份 PDF 文件由几十、几百页组成,其中包含几十个模块,这份 PDF 背后的前端页面的代码该怎么维护?页面性能怎么保障?

:首先,这么庞大的一个页面的前端代码,基本上是非常难维护的;至于性能问题,可以通过滚动懒加载的方案来解决,但这个优化本来是没必要的,完全是由于现有的 PDF 生成服务能力不足导致的。

分析

我们在做架构设计时,不论是一个系统,还是一个项目,亦或是一个页面甚至一个组件一个方法,都会尽量去避免模块过于复杂,导致难以维护,所以为了更好的可维护性,会尽量将内容进行拆分,比如微服务、组件化。

一个包含几十个模块的页面,不论你怎么去组件化,都避免不了这个页面的庞大,做的再极致,一个由几十个组件组成的页面都是难以维护的,而且如果不做滚动懒加载,这个页面首屏的性能会非常差。当下我们的用户就面临这样的问题,因为我们的 PDF 内容页必须是由一个前端页面构成。

所以,就在想,怎么才能让我们的用户不这么难受呢?开发 PDF 需求,就像开发普通的 Web 项目一样(这句话我们 PDF 生成(1)— 开篇 的技术选型中就提过),代码可以按照业务逻辑进行合理的划分,而不是全部模块堆叠在一个页面上。

其实,经过上面的问题和分析之后,解决方向很明确:PDF 生成服务不应该限制用户对于项目的设计和编码,所以,PDF 的内容页应该支持多页面。但怎么支持呢?

方案限制

  • puppeteer 生成 PDF 文件,只能是一个页面对应一份 PDF 文件,这是最底层的限制。page.gotopage.pdf都是针对当前页面的(浏览器的打印功能,只能打印当前渲染的页面)
  • 目录页方案的限制
    • 页面跳转能力是基于 HTML 锚点实现的,意味着相关 DOM 必须在一个页面中
    • 目录项对应的页码是通过 DOM 节点在页面中的位置(高度)来计算的,所以如果 DOM 位于不同的页面就意味着没办法计算了

这两个既是限制,也是进一步迭代的大前提。也就是说,我们现有的能力(大框架)不能动,也没办法动。PDF 内容页必须只能对应一个前端页面,至少在 puppeteer 层面是这样的

怎么做?

PDF 生成服务是基于 puppeteer 来实现的,也就是说 puppeteer 和用户之间还隔着一个 PDF 生成服务。那如果在 PDF 生成服务上增加一个胶水层呢?即 PDF 生成服务将用户提供的众多内容页合并成一个,然后将合并后的页面提供给 puppeteer。这是在现有技术架构上做加法,完全不影响现有技术方案和效果。

简单来讲就是:

  • 首先,通过 page.goto 方法依次打开用户提供的众多内容页,并拿到这些内容页的 HTML 信息
  • 然后,通过 page.gogo 打开 PDF 生成服务提供的容器页面,将上一步拿到的所有 HTML 信息都填充到该容器页中
  • 最后,通过 page.pdf 方法打印填充后的容器页得到 PDF 内容页

这方案可行,但有问题,这就遇到了整套方案中第二个难点了。

难点(问题)

问题:我们将用户提供的所有页面的 HTML 都塞到了一个页面中渲染,怎么解决可能会出现的样式和 JS 冲突?

:首先,冲突问题很有可能会出现,用户有义务保证自己的页面内部不出现冲突,但她没有义务确保不同的页面不出现冲突。解决问题的关键是沙箱,PDF 生成服务需要提供一套沙箱来确保容器页中各个页面的隔离性。

沙箱

浏览器中的沙箱包括样式沙箱和 JS 沙箱,实现沙箱方式一般有以下几种:

  • JS 沙箱
    • iframe
    • 代理,比如微前端框架 qiankun 的 JS 沙箱实现方案之一就是 Proxy
  • 样式沙箱
    • iframe
    • Web Component,通过 shadow dom 将不同页面的 HTML 和 CSS 包裹起来,以实现和外部环境的隔离
    • scoped,比如 Vue 组件中的 scoped 属性,qiankun 的样式沙箱方案之一

JS 沙箱

首先,我们不需要 JS 沙箱,因为我们获取的是已经渲染好的 HTML 页面,所以会剔除掉 script 标签(打印成 PDF 文件也用不上 JS),JS 的存在反而会带来不确定性和复杂性。

样式沙箱

iframe 最简单,但浏览器的 Web 安全策略会导致我们计算页码时存在问题,因为,跨域场景下没办法操作 iframe 中的 DOM。

Web Component,其整体实现思路是:

  • 利用 Web Component 的隔离特性作为各个页面的容器,来实现页面的样式隔离
  • 通过 JS 给目录项增加点击事件,借用 JS 的能力取到 Web Component 内的目标节点,通过 scrollIntoView 滚动到对应的位置
  • 最后,在容器页面中,拼接目录、各个页面对应的 Web Component 组件。

这套方案在浏览器场景中没有任何问题,而且也比较简单,但生成 PDF 就有问题了,因为生成 PDF 文件后,JS 的能力就丢了,之前的目录跳转是依靠原生的 HTML 锚点能力,现在有了 Web Component 的隔离,a 标签的 href 就取不到 Web Component 内部的元素了。但是,Web Component 实在是一个不错的样式沙箱方案,其实现思路如下,以后有机会可以在浏览器中使用:

/*** 生成 PDF 内容页* @param { Array<htmlElStr> } htmlElList */
function generatePdfContent(htmlElList) {// 定义 Web Component,用来承载 PDF 内容class PDFContent extends HTMLElement {constructor() {super()this.shadow = this.attachShadow({ mode: 'open' })}connectedCallback() {const htmlStr = this.getAttribute('html-content')this.shadow.innerHTML = htmlStr}}customElements.define('pdf-content', PDFContent)// 向 页面内 添加 pdf-content 组件const fragment = document.createDocumentFragment()for (let i = 0; i < htmlElList.length; i++) {const pdfContentEl = document.createElement('pdf-content')pdfContentEl.setAttribute('html-content', htmlElList[i])fragment.appendChild(pdfContentEl)}document.body.appendChild(fragment)
} Ï/*** 为目录设置锚点。这里的锚点跳转是通过 JS 的 scrollIntoView 来实现的*/
function setAnchorPointForDir() {// 获取目录页所有的 a 标签const links = document.querySelectorAll('.pdf-directory__wrapper a')links.forEach(link => {// 为每个目录项添加点击事件link.addEventListener('click', function (e) {// 阻止元素的默认行为 —— a 标签的链接跳转行为e.preventDefault()// 获取被点击目录项的 href 属性,是一个 id 选择器,比如: #xxconst targetId = link.getAttribute('href')// 找到页面上所有的 pdf-content 元素,这些元素是 web componentconst pdfContentComps = document.querySelectorAll('pdf-content')// 遍历这些 web component,从 web component 里查找对应的元素(目录上的 id 选择器),找到后将目标元素滚动到屏幕中间for (let i = 0, len = pdfContentComps.length; i < len; i++) {const targetElement = pdfContentComps[i].shadowRoot.querySelector(targetId)if (targetElement) {targetElement.scrollIntoView({ behavior: 'smooth' })break;}}})})
}

所以,样式沙箱,就只剩方案三 —— Scoped,这里我们借鉴 qiankun 的实验性样式隔离方案,以页面为维度,为页面中的所有样式规则增加一个特殊的选择器来限定其影响范围,因此改写后的样式会变成如下结构:

/* 原始样式 */
.app-main {font-size: 14px;color: #EFEFEF;
}/* 改写后的样式 */
.sandbox-cae17ae7-ad3a-7269-b9a0-07da189346a7 .app-main {font-size: 14px;color: #EFEFEF;
}

到这里,整个方案分析就结束了,接下来就进入实操阶段。

实战

  • 新建第二个内容页 /fe/second-content-page.html,并制造和第一个内容页的样式冲突(body 选择器)
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>第二个内容页</title><style>* {margin: 0;padding: 0;}body {width: 100%;height: 1684px;/* 和 exact-page-num.html 的背景色不一样,但都是样式选择器都是 body */background-color: green;}.anchor-wrapper1 {width: 100%;height: 1400px;}#second-content-page-anchor1 {color: red;break-before: page;}#second-content-page-anchor2 {color: blue;break-before: page;}</style>
</head>
<body><div class="anchor-wrapper1"><h1 id="second-content-page-anchor1">第二个内容页 —— 锚点 1</h1></div><div class="anchor-wrapper2"><h1 id="second-content-page-anchor2">第二个内容页 —— 锚点 2</h1></div>
</body>
</html>
  • 新建 PDF 内容页的容器页面 /fe/pdf-content.html,来承载目录和众多 PDF 内容页的 HTML + CSS
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>PDF 生成服务</title><meta name="description" content="PDF 内容页的容器页面,内容全部由 PDF 生成服务中的 JS 动态添加,由 目录 和 众多 PDF 内容页组成">
</head>
<body>
</body>
</html>
  • 改动 /server/index.mjs,由于代码量太大,就不像之前一样贴详细的改动逻辑了,以主流程为主,另外为了方便演示,相关代码都放在了一个文件中,没有进一步模块化,详细代码大家可以通过 github 访问,顺便 Star 一下呗

image.png
image.png
image-20240308131357531
image.png

PDF 内容页生成过程如下,特别是最后多页面合并后的效果(目录、页面 1 和 页面 2)

Mar-03-2024 19-44-16.gif

最终的 PDF 效果如下:

image.png
image.png
image.png
image.png
image.png
image.png
image.png

总结

我们再来回顾一下本文:

  • 首先,PDF 内容页只能由一个前端页面构成,这样的限制在复杂 PDF 文件中会给接入方的前端项目带来代码可维护性问题
  • 接着,我们通过在 PDF 服务中引入胶水层,支持将多个页面黏合成一个页面,然后交给 puppeteer 来打印
  • 然后,讲了在浏览器中沙箱的实现方案,并通过样式沙箱来解决多页面黏合后出现的样式冲突问题

到目前为止,整套 PDF 生成方案基本完成了:

  • 我们通过 PDF 文件合并技术让一份 PDF 文件包含封面、内容页和尾页三部分
  • 通过在内容页的开始位置动态插入 HTML 锚点、页面缩放、锚点元素高度计算、换页高度补偿等技术让 PDF 文件拥有了包含准确页码 + 页面跳转能力的目录页
  • 通过多页面合并技术 + 样式沙箱解决了用户在复杂 PDF 场景下前端代码维护问题,让用户的开发更自由、更符合业务逻辑

至此,PDF 生成的能力齐了,但怎么给用户使用呢?接下来我们会再用一篇来讲 PDF 生成的服务化和配置化,这样整个方案就彻底完善了。

链接

  • PDF 生成(1)— 开篇 中讲解了 PDF 生成的技术背景、方案选型和决策,以及整个方案的技术架构图,所以后面的几篇一直都是在实现整套技术架构
  • PDF 生成(2)— 生成 PDF 文件 中我们通过 puppeteer 来生成 PDF 文件,并讲了自定义页眉、页脚的使用和其中的。本文结束之后 puppeteer 在 PDF 文件生成场景下的能力也基本到头了,所以,接下来的内容就全是基于 puppeteer 的增量开发了,也是整套架构的核心难点
  • PDF 生成(3)— 封面、尾页 通过 PDF 文件合并技术让一份 PDF 文件包含封面、内容页和尾页三部分。
  • PDF 生成(4)— 目录页 通过在内容页的开始位置动态插入 HTML 锚点、页面缩放、锚点元素高度计算、换页高度补偿等技术让 PDF 文件拥有了包含准确页码 + 页面跳转能力的目录页
  • PDF 生成(5)— 内容页支持由多页面组成 通过多页面合并技术 + 样式沙箱解决了用户在复杂 PDF 场景下前端代码维护问题,让用户的开发更自由、更符合业务逻辑
  • PDF 生成(6)— 服务化、配置化 就是本文了,本系列的最后一篇,以服务化的方式对外提供 PDF 生成能力,通过配置服务来维护接入方的信息,通过队列来做并发控制和任务分类
  • 代码仓库 欢迎 Star

当学习成为了习惯,知识也就变成了常识。 感谢各位的 关注点赞收藏评论

新视频和文章会第一时间在微信公众号发送,欢迎关注:李永宁lyn

文章已收录到 github 仓库 liyongning/blog,欢迎 Watch 和 Star。

相关文章:

PDF 生成(5)— 内容页支持由多页面组成

当学习成为了习惯&#xff0c;知识也就变成了常识。 感谢各位的 关注、点赞、收藏和评论。 新视频和文章会第一时间在微信公众号发送&#xff0c;欢迎关注&#xff1a;李永宁lyn 文章已收录到 github 仓库 liyongning/blog&#xff0c;欢迎 Watch 和 Star。 回顾 在本篇开始…...

day 51 115.不同的子序列 583. 两个字符串的删除操作 72. 编辑距离

115. 不同的子序列 给你两个字符串 s 和 t &#xff0c;统计并返回在 s 的 子序列 中 t 出现的个数&#xff0c;结果需要对 109 7 取模。 示例 1&#xff1a; 输入&#xff1a;s "rabbbit", t "rabbit" 输出&#xff1a;3 解释&#xff1a; 如下所示,…...

http包详解

http包的作用及使用 go的http包是go的web编程的核心内容&#xff0c;go的web框架本质上都是基于http提供的组件进行再度封装。我们来看一下http基本的使用&#xff1a; func main() {http.Handle("/get", GetVal())http.Handle("/hello", Hello())http.H…...

Reqable实战系列:Flutter移动应用抓包调试教程

Flutter应用网络请求调试一直是业内难题&#xff0c;原因在于Dart语言标准库的网络请求不会走Wi-Fi代理&#xff0c;常规通过配置Wi-Fi代理来抓包的方式行不通。这给我们日常开发测试造成了很大的阻碍&#xff0c;严重降低工作效率。因此写一篇教程&#xff0c;讲解如何使用Req…...

乾元通渠道商中标吴忠市自然灾害应急能力提升项目

近日&#xff0c;乾元通渠道商中标宁夏回族自治区吴忠市自然灾害应急能力提升项目&#xff0c;乾元通作为设备厂家&#xff0c;为项目提供通信指挥类装备&#xff08;多链路聚合设备&#xff09;QYT-X1。 青岛乾元通数码科技有限公司作为国家应急产业企业&#xff0c;深耕于数据…...

护网蓝队面试

一、sql注入分类 **原理&#xff1a;**没有对用户输入项进行验证和处理直接拼接到查询语句中 查询语句中插⼊恶意SQL代码传递后台sql服务器分析执行 **从注入参数类型分&#xff1a;**数字型注入、字符型注入 **从注入效果分&#xff1a;**报错注入、布尔注入、延时注入、联…...

【高考志愿】金融学

目录 一、金融学类专业概述 二、主要课程 三、就业前景与方向 四、适合人群 五、金融学学科排名 六、总结 高考志愿选择金融学&#xff0c;无疑是一个既充满挑战又极具前景的决策。金融学&#xff0c;作为经济学门类下的重要分支&#xff0c;不仅涵盖了广泛的金融领域知识…...

返利App的用户行为分析与数据驱动决策

返利App的用户行为分析与数据驱动决策 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;今天我们将深入探讨返利App中的用户行为分析与数据驱动决策的技术细节和实…...

python基础:高级数据类型:集合

1、集合的定义 集合是一个无序且无重复元素的列表。其定义与数学定义一致。其无序和不重复和字典特征类似&#xff0c;但是无“值”。 2、集合的创建 集合一般由列表创建&#xff0c;在初始化列表时保证其元素唯一性&#xff0c;即为集合。 创建方法&#xff1a;x set(list…...

idk17配置

只需要把zip包解压&#xff0c;然后配置环境变量&#xff1a; bin目录路径粘到path里面就好了 然后打开cmd窗口分别输入 java javac java -version 验证...

Java实现日志全链路追踪.精确到一次请求的全部流程

广大程序员在排除线上问题时,会经常遇见各种BUG.处理这些BUG的时候日志就格外的重要.只有完善的日志才能快速有效的定位问题.为了提高BUG处理效率.我决定在日志上面优化.实现每次请求有统一的id.通过id能获取当前接口的全链路流程走向. 实现效果如下: 一次查询即可找到所有关…...

你敢相信吗,AI绘画正在逐渐取代你的工作!

前言 在当今信息技术高速发展的时代&#xff0c;AI绘画技术的崛起已引起了广泛关注和讨论。许多人开始担心AI技术是否会逐渐取代传统绘画师的工作。人类无疑是感性的动物&#xff0c;创作出来的艺术作品常常带有浓郁的个人风格和情感。但AI绘画在某些方面的突破&#xff0c;使…...

博途PLC轴工艺对象随动误差监视功能

S7-1200PLC和V90总线伺服通过工艺对象实现定位控制时在组态工艺对象里有这样的随动误差监视功能介绍,关于这个功能,今天我们解读下,工艺对象组态编程可以参考下面文章链接: S7-1200PLC和V90总线伺服通过工艺对象实现定位控制(标准报文3应用)_v90工艺对象3号报文-CSDN博客文…...

《昇思25天学习打卡营第24天 | 昇思MindSporeResNet50图像分类》

24天 本节学习了使用ResNet50网络对CIFAR-10数据集进行分类。 步骤&#xff1a; 1.数据集准备与加载 2.构建网络 残差网络结构(Residual Network)是ResNet网络的主要亮点&#xff0c;ResNet使用残差网络结构后可有效地减轻退化问题&#xff0c;实现更深的网络结构设计&#x…...

糟糕的管理者都有这几个特征

在我们的职业生涯中&#xff0c;我们都期望能遇到一位英明睿智、引领团队走向辉煌的管理者。然而&#xff0c;现实往往并非总是如此美好&#xff0c;总会有一些管理能力差的人混迹其中&#xff0c;给团队带来诸多困扰。今天&#xff0c;我们就来看看糟糕的管理者身上都有哪些特…...

Python (Ansbile)脚本高效批量管理服务器和安全

1、简介 在现代 IT 基础设施中&#xff0c;管理大量服务器是一项复杂而繁琐的任务。特别是在检查服务器的存活状态以及 SSH 登录等任务上&#xff0c;手动操作非常耗时且容易出错。本文将介绍如何使用 Python 脚本实现对多台服务器的批量检查和管理&#xff0c;包括检查服务器…...

《数字图像处理与机器视觉》案例三 (基于数字图像处理的物料堆积角快速测量)

一、前言 物料堆积角是反映物料特性的重要参数&#xff0c;传统的测量方法将物料自然堆积&#xff0c;测量物料形成的圆锥表面与水平面的夹角即可&#xff0c;该方法检测效率低。随着数字成像设备的推广和应用&#xff0c;应用数字图像处理可以更准确更迅速地进行堆积角测量。 …...

Postman接口测试工具的原理及应用详解(四)

本系列文章简介&#xff1a; 在当今软件开发的世界中&#xff0c;接口测试作为保证软件质量的重要一环&#xff0c;其重要性不言而喻。随着前后端分离开发模式的普及&#xff0c;接口测试已成为连接前后端开发的桥梁&#xff0c;确保前后端之间的数据交互准确无误。在这样的背景…...

扛鼎中国AI搜索,天工凭什么?

人类的创作不会没有瓶颈&#xff0c;但AI的热度可不会消停。 大模型之战依旧精彩&#xff0c;OpenAI选择在Google前一天举行发布会&#xff0c;两家AI企业之间的拉扯赚足了热度。 反观国内&#xff0c;百模大战激发了大家对于科技变革的热切期盼&#xff0c;而如今行业已逐渐…...

【Ant Design Vue的更新日志】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…...

Elasticsearch环境搭建|ES单机|ES单节点模式启动|ES集群搭建|ES集群环境搭建

文章目录 版本选择单机ES安装与配置创建非root用户导入安装包安装包解压配置JDK环境变量配置single-node配置JVM参数后台启动|启动日志查看启动成功&#xff0c;访问终端访问浏览器访问 Kibana安装修改配置后台启动|启动日志查看浏览器访问 ES三节点集群搭建停止es服务域名配置…...

System.currentTimeMillis() JAVA 转C#

JAVA中的System.currentTimeMillis() &#xff0c;指获取当前时间与1970年1月1日00:00:00 GMT之间所差的毫秒数的方法。 这个方法返回的是一个long类型的值&#xff0c;表示从某个固定时间点&#xff08;通常是UNIX纪元&#xff0c;即1970年1月1日00:00:00 GMT&#xff09;到…...

人机交互新维度|硕博电子发布双编码器操作面板、无线操作面板等新品

6月15日&#xff0c;硕博电子召开了一场新品发布会&#xff0c;向业界展示了多项前沿技术成果&#xff0c;其中备受瞩目的当属SPM-KEYP-D08双编码器操作面板、SPM-KEYP-D16W无线操作面板、SPR-HT-XK12A无线手持发射端以及SPQ-WT-B01洒水车专用控制面板。这些创新产品的亮相&…...

简单shell

目录 预备知识 fork 进程等待 wait waitpid 环境变量 概念 分类 常见的环境变量及其用途 环境变量的查看与设置 exec系列 函数解释 命名理解 简单shell 预备知识 fork fork 是 Linux 和许多其他类 Unix 系统中的一个重要系统调用&#xff0c;它用于创建一个新的…...

Spring Boot + FreeMarker 实现动态Word文档导出

Spring Boot FreeMarker 实现动态Word文档导出 在现代企业应用中&#xff0c;文档自动化生成是一项提升工作效率的重要功能。Spring Boot与FreeMarker的组合&#xff0c;为开发者提供了一个强大的平台&#xff0c;可以轻松实现动态Word文档的导出。本文将指导你如何使用Sprin…...

3D生物打印的未来:多材料技术的突破

多材料生物打印技术是近年来发展迅速的一项技术&#xff0c;为组织工程和再生医学带来了新的机遇&#xff0c;可以帮助我们更好地理解人体组织的结构和功能&#xff0c;并开发新的治疗方法。 1. 组织构建 复杂性模拟&#xff1a;多材料生物打印技术能够构建具有层次结构和异质…...

充电宝口碑哪个好?好用充电宝品牌有哪些?好用充电宝推荐

充电宝作为我们日常生活和出行的重要伙伴&#xff0c;其品质和性能直接影响着我们的使用体验。今天&#xff0c;就来和大家探讨一下充电宝口碑哪个好&#xff0c;为大家盘点那些备受赞誉的好用充电宝品牌&#xff0c;并向您推荐几款值得入手的充电宝&#xff0c;外出时不再担心…...

Pytorch-----(6)

一 、问题 如何计算基于不同变量的操作如矩阵乘法。 二、具体实现 0.4版本以前&#xff0c;张量是包裹在变量之中的&#xff0c;后者有三个属性grad、volatile和 requires_grad属性。&#xff08;grad 就是梯度属性&#xff0c;requires_grad属性就是 是否需要存储梯度&#x…...

leetcode hot100 第三题:最长连续序列(Java)

给定一个未排序的整数数组 nums &#xff0c;找出数字连续的最长序列&#xff08;不要求序列元素在原数组中连续&#xff09;的长度。 请你设计并实现时间复杂度为 O(n) 的算法解决此问题。 示例 1&#xff1a; 输入&#xff1a;nums [100,4,200,1,3,2] 输出&#xff1a;4 解…...

利用Jaspar进行转录因子结合位点预测

前期我们介绍了如何进行ChIP-qPCR验证&#xff0c;里面提到了一个比较重要的因素——扩增范围的选择及引物的设计。相比双荧光素酶、酵母单杂-点对点验证等允许完整启动子验证的实验&#xff0c;ChIP-qPCR要求单次验证的范围尽量控制在150-200bp内。但一个基因的启动子一般有2-…...

Ubuntu添加系统字体

&#xff08;2024.6.30&#xff09; 系统字体保存路径在/usr/share/fonts下&#xff0c;如果此目录下缺少字体&#xff0c;则使用其他可视化api&#xff08;如Python的pygame库&#xff09;的默认配置时可能会出现乱码问题。 往Ubuntu中添加字体的方法 方法一&#xff1a;手…...

深度学习相关概念及术语总结2

目录 76.AUC77.DBSCAN聚类78.贝叶斯个性化排序79.BPRBandit算法 76.AUC AUC&#xff08;Area Under the Curve&#xff09;是一种常用的评价指标&#xff0c;用于衡量分类模型的性能。AUC值代表了模型在不同阈值下的真阳性率&#xff08;True Positive Rate&#xff09;和假阳…...

基于改进滑模、经典滑模、最优滑模控制的永磁同步电机调速系统MATLAB仿真

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; 模型简介 针对永磁同步电机调速系统的响应性能和抗干扰能力问题&#xff0c;本文做了四个仿真&#xff0c;分别为&#xff1a;永磁同步电机的PID控制调速系统、基于传统滑模控制的永磁同步电机的调速系统、最…...

windows环境下创建python虚拟环境

windows环境下创建python虚拟环境 使用virtualenv库创建虚拟环境&#xff0c;可使不同的项目处于不同的环境中 安装方法&#xff1a; pip install virtualenv -i https://pypi.tuna.tsinghua.edu.cn/simple pip install virtualenvwrapper-win -i https://pypi.tuna.tsinghua…...

Fragment切换没变化?解决办法在这里

大家好&#xff0c;今天跟大家分享下如何避免fragment切换失败。方法其实很简单&#xff0c;只要在onCreate方法中初始化一个默认的fragment即可。 //开始事务FragmentTransaction transaction getActivity().getSupportFragmentManager().beginTransaction();transaction.rep…...

Linux系统防火墙iptables(下)

备份与还原iptables规则设置 1、yum -y install iptables iptables-services 安装iptables软件包 2、systemctl start iptables.service 开启服务 3、systemctl enable iptables.service 开机自启 我们对iptables命令行中的设置&#xff0c;都是临时设置&#xff0c;只要遇到服…...

你需要精益管理咨询公司的N+1个理由

近年来&#xff0c;精益管理作为一种被全球众多知名企业验证过的成功管理模式&#xff0c;越来越受到企业的青睐。但是&#xff0c;为何在实施精益管理的过程中&#xff0c;众多企业纷纷选择请咨询公司来协助呢&#xff1f;今天&#xff0c;我们就来一起揭秘这背后的原因。 1. …...

[机器学习]-3 万字话清从传统神经网络到深度学习

神经网络&#xff08;Neural Networks, NNs&#xff09;是机器学习的一种重要方法&#xff0c;灵感来源于生物神经系统&#xff0c;由大量互联的节点&#xff08;称为神经元或单元&#xff09;组成&#xff0c;通过调整这些节点间的连接权重来学习和表示复杂的非线性关系。传统…...

网络安全等级保护2.0(等保2.0)全面解析

一、等保2.0的定义和背景 网络安全等级保护2.0&#xff08;简称“等保2.0”&#xff09;是我国网络安全领域的基本制度、基本策略、基本方法。它是在《中华人民共和国网络安全法》指导下&#xff0c;对我国网络安全等级保护制度进行的重大升级。等保2.0的发布与实施&#xff0c…...

用Lobe Chat部署本地化, 搭建AI聊天机器人

Lobe Chat可以关联多个模型&#xff0c;可以调用外部OpenAI, gemini,通义千问等, 也可以关联内部本地大模型Ollama, 可以当作聊天对话框消息框来集成使用 安装方法参考&#xff1a; https://github.com/lobehub/lobe-chat https://lobehub.com/zh/docs/self-hosting/platform/…...

基于ARM的通用的Qt移植思路

文章目录 实验环境介绍一、确认Qt版本二、确认交叉编译工具链三、配置Qt3.1、修改qmake.conf3.2、创建autoConfig.sh配置文件 四、编译安装Qt五、移植Qt安装目录六、配置Qt creator6.1、配置qmake6.2、配置GCC编译器6.3、配置G编译器6.4、配置编译器套件6.5、创建应用 七、总结…...

IT专业入门,高考假期预习指南

七月来临&#xff0c;各省高考分数已揭榜完成。而高考的完结并不意味着学习的结束&#xff0c;而是新旅程的开始。对于有志于踏入IT领域的高考少年们&#xff0c;这个假期是开启探索IT世界的绝佳时机。 一、基础课程预习指南 IT专业是一个广泛的领域&#xff0c;涵盖了从软件开…...

芯片详解——AD7606C

芯片详解——AD7606C AD7607C 是一款由 Analog Devices(模拟器件公司)生产的 6 通道同步采样模数转换器(ADC),适用于高速数据采集系统。 工作原理 AD7607C 的工作原理主要包括以下几个步骤: 模拟信号输入:AD7607C 有六个模拟输入通道,可以同时进行采样。这些模拟信号…...

IDEA 编译单个Java文件

文章目录 一、class文件的生成位置二、编译单个文件编译项目报错Error:java: 无效的源发行版: 8 一、class文件的生成位置 file->project structure->Modules 二、编译单个文件 选中文件&#xff0c;点击recompile 编译项目报错 Error:java: 无效的源发行版: 8 Fi…...

人工智能业务分析

人工智能业务分析的组成图 #mermaid-svg-SKV0WrbMSANzQz4U {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SKV0WrbMSANzQz4U .error-icon{fill:#552222;}#mermaid-svg-SKV0WrbMSANzQz4U .error-text{fill:#552222;s…...

随机文本生成器

目录 开头程序程序的流程图程序打印的效果(不必细看&#xff0c;因为字符太多)例1例2例3 结尾 开头 大家好&#xff0c;我叫这是我58。看&#xff01;这下面有一个程序。 程序 #define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <random> #includ…...

java中输入输出流的继承关系

在 Java 中,输入输出流的继承关系主要围绕两个抽象基类展开:字节流基类 InputStream 和 OutputStream,以及字符流基类 Reader 和 Writer。这些类形成了 Java I/O 系统的基础,提供了丰富的子类以适应不同的输入输出需求。 字节流 字节流用于处理原始的二进制数据。 Input…...

c++应用网络编程之一基本介绍

一、网络编程介绍 c编程的应用场景在前面分析过&#xff0c;一个重要的方向就是网络编程。一般来说&#xff0c;开发者说的服务端编程在c方向上简单的可以认为是网络编程。首先需要说明的&#xff0c;本系列不对网络编程的相关基础知识展开详细的说明&#xff0c;因为这种知识…...

Web后端开发概述环境搭建项目创建servlet生命周期

Web开发概述 web开发指的就是网页向后再让发送请求,与后端程序进行交互 web后端(javaEE)程序需要运行在服务器中 这样前端才可以对其进行进行访问 什么是服务器? 解释1: 服务器就是一款软件,可以向其发送请求,服务器会做出一个响应.可以在服务器中部署文件&#xff0c;让…...

Java 位运算详解

位运算是一种直接在二进制位上进行操作的方式。位运算符包括按位与 (&)、按位或 (|)、按位异或 (^)、按位非 (~)、左移 (<<)、右移 (>>) 和无符号右移 (>>>)。这些操作符用于操作整型数据类型&#xff0c;如 int 和 long。 一、按位与 (&) 按位…...

探索QCS6490目标检测AI应用开发(二):摄像头视频的拉取和解码

作为《探索QCS6490目标检测AI应用开发》文章&#xff0c;紧接上一期&#xff0c;我们介绍如何在应用程序中拉取视频流&#xff0c;并且用硬件解码&#xff0c;得到逐帧的图像画面。我们使用了高通的Intelligent Multimedia SDK&#xff08;IM SDK&#xff09;完成视频的拉流和硬…...

声音音频文件波谱可视化展示

1、简单图形展示 import matplotlib.pyplot as plt import numpy as np import torch import torchaudiodef plot_waveform(waveform, sample_rate, title"Waveform", xlimNone, ylimNone):waveform waveform.numpy()num_channels, num_frames waveform.shapetime…...

k8s自动补全工具和UI管理界面

分享两个有利于K8S的工具 目录 分享两个有利于K8S的工具 一、部署Dashboard&#xff08;主节点&#xff09; 介绍 1.1、查看集群状态 1.2、下载yaml文件并运行Dashboard 1.3、部署服务 1.4、创建访问账户、获取token&#xff08;令牌&#xff09; 1.5、浏览器访问Dash…...

基于Hadoop平台的电信客服数据的处理与分析③项目开发:搭建基于Hadoop的全分布式集群---任务7:格式化并启动Hadoop集群

任务描述 任务内容为格式化并启动Hadoop集群&#xff0c;并修复可能出现的Bug。 任务指导 Hadoop集群启动前需要在NameNode上格式化元数据&#xff0c;成功格式化后才能启动Hadoop的HDFS和YARN。 格式化启动Hadoop集群的步骤如下&#xff1a; 1. 在NameNode&#xff08;ma…...

多模态融合 + 慢病精准预测

多模态融合 慢病精准预测 慢病预测算法拆解子解法1&#xff1a;多模态数据集成子解法2&#xff1a;实时数据处理与更新子解法3&#xff1a;采用大型语言多模态模型&#xff08;LLMMs&#xff09;进行深度学习分析 慢病预测更多模态 论文&#xff1a;https://arxiv.org/pdf/2406…...

小白学Frp+Proxifier | 网隧道搭建

一、什么是Frp和Proxifier Frp&#xff08;Fast Reverse Proxy&#xff09;&#xff1a;是一款高性能的反向代理应用&#xff0c;可以帮助你将内网服务暴露到外网。 Proxifier&#xff1a;是一款网络代理软件&#xff0c;允许网络应用通过HTTP、SOCKS代理连接互联网。 二、准…...

必胜客之后,DQ冰淇淋也跨界卖汉堡了

汉堡界又迎来一重磅新玩家。近日,DQ冰淇淋在其官方微博、小红书等社交媒体上发文称,DQ汉堡全国首店将于7月10日登陆上海。新玩家入局同时,哈比特汉堡、摩斯汉堡等一批“老玩家”却遗憾陆续退出中国市场。汉堡界,似乎从来不缺新故事。01.冰淇淋“专家”卖汉堡29元起卖,不“…...

上市即降价?第9代凯美瑞17.18万起!

上市即降价?合资车也扛不住这波降价潮了,第9代凯美瑞上市,17.18万起,进一步降低入门门槛。大家好,我是五一不放假!凯美瑞已经有40几年的历史,今年上市的第九代,外观设计和产品力方面都得到全面的升级。共推出9款车型,有汽油版和电混双擎版,指导价是17.18-20.68万元。…...

new CCDIKSolver( OOI.kira, iks ); // 创建逆运动学求解器

demo案例 new CCDIKSolver(OOI.kira, iks); 在使用某个特定的库或框架来创建一个逆运动学&#xff08;Inverse Kinematics, IK&#xff09;求解器实例。逆运动学在机器人学、动画和计算机图形学等领域中非常重要&#xff0c;它用于根据期望的末端执行器&#xff08;如机器人的…...

关于软件设计模式的理解

系列文章 关于时间复杂度o(1), o(n), o(logn), o(nlogn)的理解 关于HashMap的哈希碰撞、拉链法和key的哈希函数设计 关于JVM内存模型和堆内存模型的理解 关于代理模式的理解 关于Mysql基本概念的理解 关于软件设计模式的理解 文章目录 前言一、软件设计模式遵循的六大原则…...

无线麦克风什么牌子的音质效果好?一文读懂无线领夹麦克风哪款好

​在当今的数字时代&#xff0c;无线技术已经深入到我们生活的方方面面&#xff0c;无线领夹麦克风便是其中的佼佼者。它们为讲者、表演者以及那些需要在移动中讲话的人们提供了解放双手和自由移动的可能。本文旨在探讨无线领夹麦克风的多种用途&#xff0c;以及如何挑选最适合…...

LLM - 模型下载与 git-lfs 安装

目录 一.引言 二.安装 git lfs 1.使用 apt-get 安装 2.使用 Brew 安装 3.LFS 验证 三.总结 一.引言 在 HuggingFace 上下载模型时提供一个 git clone 的指令&#xff0c;执行后可以下载对应模型的模型文件: 但是本机还没有 git lfs 命令: git: lfs is not a git comman…...