在现代Web应用中集成 PDF.js (pdfjs-dist 5.2 ESM): 通过 jsdelivr 实现动态加载与批注功能的思考
PDF 文档在现代 Web 应用中越来越常见,无论是作为文档预览、报告展示还是在线编辑的载体。Mozilla 的 PDF.js 是一个功能强大的 JavaScript 库,它使得在浏览器端渲染和显示 PDF 文件成为可能,无需依赖原生插件。
本文将深入探讨如何在你的项目中使用 pdfjs-dist
库的 5.2 版本,特别关注其通过 jsdelivr 引入的 ESM (ECMAScript Module) 版本 (.mjs
文件),并在此基础上实现 PDF 文件的动态加载,同时对实现批注功能给出思路和指导。
为什么选择 pdfjs-dist 5.2 ESM 和 jsdelivr?
- pdfjs-dist: 这是 PDF.js 的发布版本,包含了核心渲染代码和相关的构建产物,方便在项目中使用。
- 5.2 版本: 选择特定版本有助于保证代码的稳定性,避免未来版本更新可能带来的兼容性问题。
- ESM (
.mjs
): ECMAScript Modules 是现代 JavaScript 的标准模块系统。使用 ESM 可以更好地组织代码、提高性能(如 tree shaking)并避免全局变量污染。它需要现代浏览器的支持,并通过<script type="module">
标签引入。 - jsdelivr: 这是一个免费的、快速的 CDN (Content Delivery Network),可以直接从 npm 包获取文件。使用 CDN 可以加速库的加载,减轻自己服务器的压力。
第一步:核心集成 - 引入 pdfjs-dist
ESM 版本
使用 ESM 格式引入库需要在你的 HTML 文件中做一些调整。我们需要引入 pdf.min.mjs
(核心库)和 pdf.worker.min.mjs
(用于在 Web Worker 中执行耗时任务)。
在你的 HTML 文件中,使用 <script type="module">
标签来编写或引用你的 JavaScript 代码:
<!DOCTYPE html>
<html>
<head><title>PDF.js ESM Integration</title><meta charset="UTF-8"><style>/* 可以添加一些基本的样式 */#pdf-viewer-container {width: 800px; /* 根据需要设置容器宽度 */margin: 20px auto;border: 1px solid #ccc;overflow: auto; /* 如果PDF很大需要滚动 */}/* 其他样式将在后续步骤中添加 */</style>
</head>
<body><h1>PDF.js ESM Example</h1><!-- 你的 PDF 查看器 UI 元素将在这里 --><!-- 使用 type="module" 引入你的主要 JavaScript 文件 --><!-- 假设你的主要逻辑在 main.js 中 --><script type="module" src="./main.js"></script></body>
</html>
在你的 main.js
文件中,使用 import
语句从 jsdelivr 引入 pdfjs-dist
:
// 从 jsdelivr 引入 pdfjs-dist 的核心模块
// 使用具体的版本号 5.2.133 (这是一个示例版本号,请根据实际需要调整)
import { getDocument, GlobalWorkerOptions } from 'https://cdn.jsdelivr.net/npm/pdfjs-dist@5.2.133/build/pdf.min.mjs';// 设置 workerSrc,这是 pdfjs-dist 必须的配置
// workerSrc 应该指向 pdf.worker.min.mjs 文件,且版本应与主库一致
GlobalWorkerOptions.workerSrc = 'https://cdn.jsdelivr.net/npm/pdfjs-dist@5.2.133/build/pdf.worker.min.mjs';// 现在你可以使用导入的 getDocument 函数来加载 PDF
// 例如加载一个远程 PDF 文件 (这将在下一节详细展开)
/*
async function loadSamplePdf() {const samplePdfUrl = 'https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf';try {const loadingTask = getDocument(samplePdfUrl);const pdfDocument = await loadingTask.promise;console.log('Sample PDF loaded:', pdfDocument);// TODO: 渲染 PDF 页面} catch (error) {console.error('Error loading sample PDF:', error);}
}loadSamplePdf(); // 页面加载后尝试加载一个示例 PDF
*/
解释:
<script type="module" src="./main.js">
:告诉浏览器加载./main.js
文件作为一个 ES 模块。import { getDocument, GlobalWorkerOptions } from '...'
:使用 ESM 导入语法,从 jsdelivr 上的pdf.min.mjs
导入getDocument
函数和GlobalWorkerOptions
对象。GlobalWorkerOptions.workerSrc = '...'
:非常重要,这配置了 PDF.js worker 脚本的 URL。worker 负责在后台处理 PDF 的解析,避免阻塞主线程,提高用户体验。
第二步:实现 PDF 文件的动态加载与渲染
在实际应用中,你通常需要根据用户的操作(例如选择文件或输入 URL)来加载不同的 PDF 文件。我们需要一个 HTML 结构来接收用户输入,并编写 JavaScript 代码来处理加载和渲染过程。
扩展你的 HTML (在 <body>
内):
<!-- ... (head and previous body content) ... -->
<body><h1>My Dynamic PDF Viewer</h1><input type="file" id="pdfFilePicker" accept="application/pdf"><button id="loadPdfButton">Load Selected PDF</button><button id="loadUrlPdfButton">Load Sample PDF from URL</button><div id="pdf-viewer-container"><!-- PDF 页面将渲染到这里 --></div><script type="module" src="./main.js"></script>
</body>
</html>
添加必要的 CSS (在 <style>
标签内):
/* ... (previous styles) ... */
.pdfPage {margin-bottom: 10px; /* 页与页之间的间距 */border-bottom: 1px solid #eee; /* 页之间分隔线 */position: relative; /* 为后续添加批注层做准备 */box-shadow: 0 0 8px rgba(0,0,0,0.1); /* 添加一些阴影效果 */
}
.pdfPage canvas {display: block; /* 防止 canvas 下方出现空白 */margin: 0 auto; /* canvas 居中 */
}
/* 批注层样式,如果需要 */
.annotationLayer {position: absolute;top: 0;left: 0;width: 100%;height: 100%;pointer-events: none; /* 默认不捕获鼠标事件,除非需要交互 */overflow: hidden; /* 避免批注超出页面边界 */
}
修改 main.js
来实现动态加载和渲染逻辑:
// ... (import and workerSrc setup) ...const pdfViewerContainer = document.getElementById('pdf-viewer-container');
const pdfFilePicker = document.getElementById('pdfFilePicker');
const loadPdfButton = document.getElementById('loadPdfButton');
const loadUrlPdfButton = document.getElementById('loadUrlPdfButton');let pdfDocument = null; // 存储当前加载的 PDF 文档对象// 函数:渲染单个页面
async function renderPage(pageNum, pdfDocument) {if (!pdfDocument) return; // 确保文档已加载try {const page = await pdfDocument.getPage(pageNum);const scale = 1.5; // 渲染比例,可以根据需要调整const viewport = page.getViewport({ scale: scale });// 创建一个 div 容器用于包裹 canvas 和其他层 (如批注层)const pageDiv = document.createElement('div');pageDiv.className = 'pdfPage';// 设置 pageDiv 的尺寸以匹配渲染后的页面尺寸pageDiv.style.width = `${viewport.width}px`;pageDiv.style.height = `${viewport.height}px`;pageDiv.dataset.pageNumber = pageNum; // 存储页码方便后续查找// 创建 canvas 元素用于渲染 PDF 内容const canvas = document.createElement('canvas');const context = canvas.getContext('2d');canvas.height = viewport.height;canvas.width = viewport.width;pageDiv.appendChild(canvas); // 将 canvas 添加到 pageDiv 中// 创建一个用于自定义批注的层 (将在第三步讨论)const annotationLayer = document.createElement('div');annotationLayer.className = 'annotationLayer';// annotationLayer.style.width = `${viewport.width}px`; // 批注层尺寸通常与 viewport 一致// annotationLayer.style.height = `${viewport.height}px`;// 批注层需要定位在 pageDiv 内部,且覆盖 canvas// 通过 CSS .annotationLayer 设置 absolute position 和 top/left 0 即可pageDiv.appendChild(annotationLayer); // 将批注层添加到 pageDiv 中pdfViewerContainer.appendChild(pageDiv); // 将 pageDiv 添加到主容器// 渲染 PDF 页面内容到 canvasconst renderContext = {canvasContext: context,viewport: viewport,// 如果需要渲染内置的文本层或批注层,可以在这里指定容器// 引入并使用 TextLayerBuilder 和 AnnotationLayerBuilder 会增加代码复杂度// textLayer: textLayerDiv,// annotationLayer: annotationLayerDiv,// annotationMode: pdfjsLib.AnnotationMode.ENABLE_FORMS,};// 执行渲染,这是一个异步操作await page.render(renderContext).promise;console.log(`Page ${pageNum} rendered.`);// TODO: 如果需要渲染内置文本层和批注层,在这里调用其 render 方法// 例如 textLayer.render(); annotationLayer.render();} catch (error) {console.error(`Error rendering page ${pageNum}:`, error);// 可以在页面位置显示一个错误消息}
}// 函数:加载并渲染整个 PDF
async function loadAndRenderPdf(pdfData) {// 清空之前的渲染内容pdfViewerContainer.innerHTML = '';pdfDocument = null; // 清除之前加载的文档对象try {// 加载 PDF 文档,pdfData 可以是 URL 字符串、ArrayBuffer、Blob 等const loadingTask = getDocument(pdfData);pdfDocument = await loadingTask.promise;console.log('PDF loaded:', pdfDocument);const numPages = pdfDocument.numPages;console.log('Number of pages:', numPages);// 循环渲染每一页for (let pageNum = 1; pageNum <= numPages; pageNum++) {// 使用 Promise.resolve() 包裹 renderPage 可以让循环继续,而无需等待每页渲染完成// 这样可以更快地显示第一页Promise.resolve().then(() => renderPage(pageNum, pdfDocument));}// TODO: 如果需要处理 PDF 元数据、大纲、缩略图等,可以在这里访问 pdfDocument 对象} catch (reason) {console.error('Error during PDF loading:', reason);alert(`Failed to load PDF: ${reason.message || reason}`); // 给用户提示}
}// 事件监听器:加载本地文件
loadPdfButton.addEventListener('click', () => {const file = pdfFilePicker.files[0];if (file) {const reader = new FileReader();reader.onload = function(e) {const arrayBuffer = e.target.result;loadAndRenderPdf(arrayBuffer); // 加载 ArrayBuffer};reader.onerror = function(e) {console.error("FileReader error:", e);alert("Error reading file.");}reader.readAsArrayBuffer(file);} else {alert('Please select a PDF file.');}
});// 事件监听器:加载示例 URL
loadUrlPdfButton.addEventListener('click', () => {const samplePdfUrl = 'https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf'; // 替换为你自己的 PDF URLloadAndRenderPdf(samplePdfUrl); // 加载 URL
});// 注意:你的 HTML 文件需要通过 Web 服务器打开 (http:// 或 https://),
// 直接用浏览器打开本地文件 (file://) 可能因为跨域问题导致 worker 加载失败或 PDF 文件无法加载。
解释上述代码:
#pdf-viewer-container
:一个容器,用于容纳所有渲染后的 PDF 页面。input[type="file"]
和button
:用于触发本地文件选择和加载示例 URL。pdfDocument
: 存储通过getDocument
加载成功后的 PDF 文档对象。loadAndRenderPdf(pdfData)
:核心函数,接收 PDF 数据(可以是 URL 或 ArrayBuffer),清空容器,调用getDocument
加载,然后遍历所有页码,为每一页调用renderPage
。renderPage(pageNum, pdfDocument)
:为指定的页码创建一个div.pdfPage
,内部包含一个canvas
用于绘制 PDF 内容,以及一个div.annotationLayer
用于后续的自定义批注。计算 viewport 并设置 canvas 尺寸,然后调用page.render()
将页面内容绘制到 canvas。- 事件监听器:分别为文件选择按钮和加载 URL 按钮添加点击事件,读取文件或指定 URL,然后调用
loadAndRenderPdf
。
至此,你已经构建了一个基本的 PDF 查看器,可以动态加载本地或远程的 PDF 文件并将其渲染到页面上。
第三步:实现自定义批注功能
重要提示: pdfjs-dist
库的主要功能是渲染 PDF 内容,包括显示 PDF 文件中已有的批注。它不提供添加、编辑或保存新的批注的功能。实现批注(如高亮、下划线、矩形框、文本框等)是一个需要在 PDF 渲染层之上自定义构建的功能。
实现自定义批注功能的整体思路是在每个 PDF 页面渲染出的 canvas
上方,叠加一个透明的 HTML 元素(我们在第二步中创建了 div.annotationLayer
),然后在这个叠加层上通过 DOM 操作、SVG 绘制或额外的 Canvas 绘制来表示批注。
以下是实现批注功能的关键步骤和考虑因素:
- 批注层管理: 确保每个 PDF 页面都有一个精确覆盖其渲染区域的批注层 (
div.annotationLayer
)。通过 CSSposition: absolute; top: 0; left: 0; width: 100%; height: 100%;
来定位。 - 用户交互:
- 工具选择: 提供 UI 元素(按钮、工具栏)让用户选择要添加的批注类型(例如,高亮、矩形、文本框、直线等)。
- 事件监听: 在每个页面的
annotationLayer
或一个委托的父容器上监听鼠标事件 (mousedown
,mousemove
,mouseup
) 或触摸事件 (touchstart
,touchmove
,touchend
)。 - 绘制反馈: 在
mousemove
/touchmove
过程中,根据用户选择的工具和鼠标/触摸位置,在批注层上实时绘制一个临时图形(例如,绘制矩形时显示一个虚线框),给用户即时反馈。
- 数据模型: 设计一个数据结构来存储每个批注的信息。这些信息至少应该包括:
- 批注所在的页码。
- 批注的类型(如 ‘highlight’, ‘rectangle’, ‘text’, ‘line’)。
- 批注在页面上的位置和尺寸信息。这通常需要将屏幕坐标转换为 PDF 页面内部的坐标系统。
- 批注的样式信息(颜色、线宽、透明度等)。
- 如果是文本批注,则需要存储文本内容。
- 坐标转换: 这是实现批注的关键难点之一。鼠标/触摸事件提供的坐标是相对于浏览器视口或页面的像素坐标。你需要将这些像素坐标转换为 PDF 页面内部的坐标(PDF 坐标系统通常以点为单位,原点在左下角)。
pdfjs-dist
提供的page.getViewport(scale).convertToPdfPoint(x, y)
方法可以将视口像素坐标转换为 PDF 坐标,而page.getViewport(scale).convertToViewportPoint(x, y)
可以将 PDF 坐标转换为视口像素坐标。在处理不同缩放比例时,正确进行坐标转换至关重要。 - 批注渲染: 当页面加载、缩放或批注数据更新时,遍历当前页面的批注数据。根据批注类型,在对应的
annotationLayer
中创建并添加相应的 HTML 元素、SVG 元素或在批注层的 Canvas 上绘制图形来显示批注。例如:- 高亮:创建
<span>
或<div>
元素,设置背景颜色和位置。 - 矩形/直线:创建 SVG 元素 (
<rect>
,<line>
) 并设置属性,或者在批注层的 Canvas 上使用 2D Context 绘制。 - 文本框:创建
<div>
或<textarea>
,设置位置和内容。
- 高亮:创建
- 批注管理界面: 实现选中批注、显示编辑框、拖动、改变大小、删除等功能。这涉及到监听批注元素的事件,更新批注数据,并重新渲染批注层。
- 数据持久化: 实现将批注数据保存到后端服务器或浏览器的本地存储中,以便下次打开同一个 PDF 时可以加载并恢复批注。批注数据通常需要与 PDF 文件本身关联(例如通过 PDF 的哈希值或文件名)。
关于改造官方 viewer.html
:
pdfjs-dist
源码中的 web/viewer.html
和 web/viewer.js
提供了一个完整的 PDF 查看器实现。虽然你可以借鉴其结构和逻辑(尤其是文本层和内置批注层的渲染方式),但直接修改和嵌入到你的项目会非常复杂。viewer.js
是为一个独立应用设计的,其内部耦合度高,依赖于许多辅助类和资源。从头开始,使用核心库构建你自己的查看器,并逐步添加所需功能(包括批注),通常是更灵活和易于维护的方式。
总结
通过 jsdelivr 引入 pdfjs-dist
的 5.2 版本 ESM 文件,你可以轻松地在现代 Web 应用中集成 PDF 查看功能。使用 <script type="module">
和 import
是 ESM 的标准方式。实现 PDF 的动态加载需要处理文件读取或 URL 请求,并通过 getDocument
和 renderPage
函数来完成。
然而,实现自定义的 PDF 批注功能是一个相对独立的任务,它建立在 PDF 渲染之上,需要你自行设计批注的数据模型、用户交互、坐标转换以及批注的渲染和管理逻辑。这部分功能需要投入额外的开发工作,并且可能需要处理复杂的细节,尤其是在保证批注位置准确性和在不同缩放级别下同步更新方面。如果你需要开箱即用的复杂批注功能,可能需要考虑集成商业的 PDF SDK。
希望这篇博文能帮助你理解如何在现代 Web 项目中集成和使用 pdfjs-dist
,并为实现动态加载和批注功能提供清晰的思路。
相关文章:
在现代Web应用中集成 PDF.js (pdfjs-dist 5.2 ESM): 通过 jsdelivr 实现动态加载与批注功能的思考
PDF 文档在现代 Web 应用中越来越常见,无论是作为文档预览、报告展示还是在线编辑的载体。Mozilla 的 PDF.js 是一个功能强大的 JavaScript 库,它使得在浏览器端渲染和显示 PDF 文件成为可能,无需依赖原生插件。 本文将深入探讨如何在你的项…...
android ViewModel liveData无法监听之多线程下activityViewModels不安全
我们一般的,会遇到liveData无法监听到结果,可能存在主要2种可能: liveData没有正确注册;liveData连续多次设置值,中间的值,会被丢弃,但最后一次是能监听到的。 但是我们容易忽略一种case&…...
【即插即用涨点模块】DSConv动态蛇形卷积:自适应聚焦细长弯曲的局部结构特征,助力分割高效提点【附源码+注释】
《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发】2.【车牌识别与自动收费管理系统开发】3.【手势识别系统开发】4.【人脸面部活体检测系统开发】5.【图片风格快速迁移软件开发】6.【人脸表表情识别系统】7.【…...
守护数字家园:个人博客安全防护指南
前言 在之前的文章《WordPress个人博客搭建(一)》《WordPress个人博客搭建(二)》《WordPress个人博客搭建(三)》中,我们已经在非凡云云服务器上,借助1Panel搭建起属于自己的数字庭院…...
课外活动:简单了解原生测试框架Unittest前置后置的逻辑
简单了解原生测试框架Unittest前置后置的逻辑 一、测试框架执行顺序解析 1.1 基础执行流程 import unittestclass A(unittest.TestCase):classmethoddef setUpClass(cls):print(f"【CLASS START】{cls.__name__}")def setUp(self):print(f"【TEST START】{se…...
带你玩转 Flink TumblingWindow:从理论到代码的深度探索
0.前言 在深入探讨 TumblingWindow 之前,我们先来了解一下流处理或流计算中“窗口”的基本概念。在数据流中,源会持续不断地生成数据,因此计算最终值是不可行的。 在大多数用例中,为了获取有意义的信息,最好使用两种方…...
Java线程安全问题深度解析与解决方案
一、线程安全问题的本质 并发编程的核心挑战:当多个线程同时访问共享资源时,由于操作系统的抢占式调度特性,可能导致不可预期的结果。这种因非原子操作和竞态条件引发的数据不一致问题,称为线程安全问题。 二、经典线程安全问题案…...
python实现的音乐播放器
python实现的音乐播放器 音乐播放器,原来写过一个简陋的例子,可见 https://blog.csdn.net/cnds123/article/details/137874107 那个不能拖动播放进度条上的滑块到新的位置播放。下面介绍的可以拖动播放进度条上的滑块到新的位置播放。 简单实用的音乐播放器 这个简单实用的…...
SMT贴片工艺核心优化与生产实践
内容概要 作为现代电子制造的核心环节,SMT贴片工艺的优化直接决定了产品可靠性与生产效率。本文系统性梳理工艺链中的关键控制点,从锡膏印刷精度到回流焊温度曲线,再到AOI检测技术升级,形成覆盖全流程的优化框架。针对行业普遍存…...
趣味编程:爱心
概述:五月十一号就是母亲节了,本篇博客主要是为母亲所写,这是属于程序员的浪漫,这篇博客是对母亲这么多年无微不至爱的情书。 目录 1. 效果展示 2. 源码展示 3. 代码逻辑详解 3.1 头文件与常量定义 3.2 心形曲线参数方程 3.…...
C语言—指针2
1. const 修饰变量 1.1 const修饰变量 变量被const修饰时,变量此时为常变量,本质为常量,语法上不可被修改,但是如果此时需要修改变量值,可以通过指针的方式修改。 虽然此时通过指针的方式确实修改了变量的值ÿ…...
66、微服务保姆教程(九)微服务的高可用性
微服务的高可用性与扩展 服务的高可用性 集群搭建与负载均衡。服务的故障容错与自愈。分布式事务与一致性 分布式事务的挑战与解决方案。使用 RocketMQ 实现分布式事务。微服务的监控与可观测性 metrics 和日志的收集与分析。sentinel 的监控功能。容器化与云原生 将微服务部署…...
主场景 工具栏 植物卡牌的渲染
前置知识:使用easyx图形库 1.IMAGE内存变量存储的是一张位图(图像),存储了像素数据(颜色,尺寸等) 2.loadimage(&变量名,"加载的文件路径")表示从文件中加载图像到变量中 3. saveimage("文件路径", &变…...
超详细!RxSwift 中的 BehaviorRelay 使用教程(含原理 + 示例 + 实战)
目录 前言 1.什么是 BehaviorRelay 2.基本使用方式 3.BehaviorRelay的常用API 4.BehaviorRelay 和其它类型的对比 5.BehaviorRelay的使用场景 1.绑定UITableView 2.MVVM 场景下使用 BehaviorRelay 6.使用注意事项以及建议 1.注意事项 2.使用建议总结 7.推荐阅读 前…...
【软件测试学习day7】Junit5
Junit 是单元测试框架,本期掌握 Junit5 的基础用法。 1. 注解 首先引入 Junit 依赖: <dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.9.1</version…...
前端实战中的单例模式:以医疗药敏管理为例
目录 一、什么是单例模式?1. 状态共享性 —— 数据唯一,任意访问,任意修改2. 生命周期控制性 —— 自己掌控何时创建、何时销毁 二、实战分析:医疗药敏管理系统中的单例应用三、其他场景示例单例实现:ConfigManager.ts…...
如何在 Logback 日志框架中加入链路 ID
在 Logback 日志框架中加入链路 ID,能有效将同一条链路的日志串联起来,便于追踪和排查问题。 1. 生成和管理链路 ID 要保证在整个请求链路里都能获取到链路 ID,可借助 ThreadLocal 来实现。以下是一个简单的工具类示例: import…...
晶振:智能设备的“心跳”如何支撑5G与航天
在现代科技飞速发展的时代,智能设备已深度融入我们生活的方方面面,而晶振,作为智能设备的“心跳”,正默默发挥着不可替代的关键作用。无论是翱翔太空的神舟飞船,还是人们手中须臾不离的智能手机,亦或是推动…...
【HarmonyOS 5】App Linking 应用间跳转详解
目录 什么是 App Linking 使用场景 工作原理 如何开发 1.开通 App Linking 2.确定域名 3.服务端部署 applinking.json 文件 4.AGC绑定域名 5.项目配置 6.组装聚合链接 7.解析聚合链接中的参数 其他 如何获取应用ID 什么是 App Linking App Linking 是一款创建跨…...
neo4j官方示例
目录 一、准备数据 1.执行查看结果 二、操作 1.find 单个节点 2.同上,已某个属性去查询 3. 指定查询个数 4.条件查询 5.查询某个人出演的电影汇总 6.查询tom出演的电影中,还有其他演员的信息。 7.查询跟电影(Cloud Atlas)有关的演员࿰…...
基于vueflow可拖拽元素的示例(基于官网示例的单文件示例)
效果图 代码 <template><div style"width: 100%;height: calc(100vh - 84px)"><VueFlow :nodes"nodes" :edges"edges" drop"onDrop" dragover"onDragOver" dragleave"onDragLeave"><div cl…...
minio单点登录与集成(免密)
需求:系统A里,需要实现与MINIO单点登录集成,也就是说,登录了系统A,在访问MINIO时不需要再输入用户密码就可以直接访问。 具体场景如下: 在系统A的一个页面里,配置一个按钮链接,点击…...
深入理解 Docker 网络原理:构建高效、灵活的容器网络
在现代软件开发中,Docker 已经成为了容器化技术的代名词,广泛应用于开发、测试和生产环境。Docker 使得开发者能够将应用及其依赖打包成一个轻量级的容器,并通过 Docker 容器化技术来实现高效的部署与管理。 然而,在日常使用 Dock…...
Hutool中的Pair类详解
1. Pair类概述 Hutool工具库中的Pair类是一个简单的键值对数据结构,用于存储两个相关联的对象。它类似于Map的Entry,但更加轻量级,适用于需要临时存储两个相关联数据的场景。 2. Pair类的主要特点 简单轻量:不依赖复杂的数据结…...
没有Mac,我是怎么上传IPA到App Store的?
没有Mac,我是怎么上传IPA到App Store的? 最近赶一个小项目上线,写的是一个Flutter做的App。安卓版本一晚上搞定,iOS上架却差点把人整崩。 不是我技术菜,是实在太麻烦了。最关键的,是我这台Windows笔电根本…...
RISC-V hardfault分析工具,RTTHREAD-RVBACKTRACE
RV BACKTRACE 简介 本文主要讲述RV BACKTRACE 的内部主要原理 没有接触过rvbacktrace可以看下面两篇文章,理解一下如何使用RVBACKTRACE RVBacktrace RISC-V极简栈回溯组件:https://club.rt-thread.org/ask/article/64bfe06feb7b3e29.html RVBacktra…...
c语言if else语句格式(非常详细)
在C语言中,if else 语句是一种常用的条件控制结构,用于根据不同条件执行不同的代码块。 if-else 语句的基本格式 if-else 语句的基本格式如下: if (条件) { // 如果条件为真,执行这里的代码 } else { // 如果条件为假&a…...
Logback官方文档翻译章节目录
Logback官方文档翻译章节目录 第一章 Logback简介 第二章 Logback的架构(一) Logback的架构(二) Logback的架构(三) 持续更新中…...
按摩椅的机芯类型和材质
按摩椅的机芯类型和材质是影响其按摩效果、使用寿命以及舒适度的重要因素。下面我将从这两个方面详细为你解析: 一、按摩椅机芯类型 按摩椅的“机芯”相当于它的“心脏”,决定了按摩手法、力度、覆盖范围等关键性能。 常见机芯类型(按技术发…...
HarmonyOS-hdc远程网络方式连接设备
hdc工具使用手册 1 hdc简介 hdc(OpenHarmony Device Connector)是为开发人员提供的用于设备连接调试的命令行工具,pc端开发机使用命令行工具hdc,该工具需支持部署在Windows/Linux/Mac等系统上与OpenHarmony设备(或模…...
秋招准备——2.跨时钟相关
格雷码异步FIFO跨时钟域处理 格雷码 一、格雷码规律 相邻性:相邻两个数的格雷码只有一位不同,例如: 0000 → 0001(仅最低位变化)0001 → 0011(仅次低位变化)0011 → 0010(仅最低位…...
【开源版】likeshop上门家政系统PHP版全开源+uniapp前端
一.系统介绍 likeshop_上门家政系统,PHP版本更新至2.1.1最新版,全开源,适用于上门家政场景,系统拥有用户端、师傅端、无论运营还是二开都是性价比极高的100%开源家政系统。 二.搭建环境-教程 系统环境:CentOS、 运行…...
Memgraph 的安装教程
目录 Memgraph 安装步骤1. 使用 Docker 安装 Memgraph2. 使用 Memgraph Lab3. 使用 Python 客户端连接 Memgraph Memgraph 安装步骤 1. 使用 Docker 安装 Memgraph Memgraph 可以通过 Docker 快速安装和运行。以下是使用 Docker 安装 Memgraph 的步骤: 安装 Docke…...
华为网路设备学习-21 路由过滤(filter-policy)
一、路由过滤(filter-policy) 1、用于控制路由更新、接收的一个工具 2、只能过滤路由信息,无法过滤LSA 二、路由过滤(filter-policy)与动态路由协议 1、距离矢量路由协议 RIP动态路由协议 交换的是路由表࿰…...
Mac 平台 字体Unicode范围分析器
字体Unicode范围分析器 #include <CoreText/CoreText.h> // CoreText框架头文件,用于字体处理 #include <CoreFoundation/CoreFoundation.h> // CoreFoundation框架头文件 #include <stdio.h> // 标准输入输出 #include…...
Android不能下载Gradle,解决方法Could not install Gradle distribution from.......
外网下载速度太慢导致失败,换成国内镜像,可加速下载: 官网地址:https://services.gradle.org/distributions/ 腾讯云镜像 Gradle下载地址:https://mirrors.cloud.tencent.com/gradle/ 阿里云镜像 Gradle下载地址&…...
树状数组的操作问题--Python
树状数组的操作问题 一、问题引入二、解题步骤1.思维导图2.解题步骤 三、代码实现1.代码2.复杂度分析 四、个人总结 一、问题引入 请编写程序,实现树状数组区间求前缀和、单点修改的操作。 输入格式: 输入首先给出一个正整数 n(2≤n<10^…...
FEKO许可限制
随着科技的飞速发展,电磁仿真软件在多个领域发挥着越来越重要的作用。FEKO作为一款业界领先的电磁仿真软件,广泛应用于通信、雷达、航空航天、电子对抗等领域。然而,为了确保软件使用的合规性与高效性,FEKO设定了相应的许可限制。…...
第5章 深度学习和卷积神经网络
深度学习是人工智能的一种实现方法。本章我们将考察作为深度学习的代表的卷积神经网络的数学结构。 5-1小恶魔来讲解卷积神经网络的结构 深度学习是重叠了很多层的隐藏层(中间层)的神经网络。这样的神经网络使隐藏层具有一定的结构,从而更加…...
window 显示驱动开发-处理内存段(一)
视频内存管理器 (VidMm) 负责管理 GPU 的地址空间。 在此之前,内核模式显示微型端口驱动程序 (KMD) 必须通过使用内存段将 GPU 的地址空间描述为 VidMm。 KMD 创建内存段以概括和虚拟化视频内存资源。 它可以根据硬件支持的存储器类型(例如,…...
QT实现曲线图缩放、拖拽以及框选放大
.h文件 protected: void saveAxisRange();void wheelEvent(QWheelEvent *event) override;void mousePressEvent(QMouseEvent *event) override;void mouseMoveEvent(QMouseEvent *event) override;void mouseReleaseEvent(QMouseEvent *event) override;private:QPoint m_…...
龙虎榜——20250508
上证假阴包阳的走势,量能较昨天有萎缩,在前期压力附近~ 深证这两天假阴包阳的走势,60分钟未突破昨天的高点,缺口也未补等待明天的选择~ 2025年5月8日龙虎榜行业方向分析 一、核心行业方向 军工航天(政策催化地缘驱动…...
SEMI E40-0200 STANDARD FOR PROCESSING MANAGEMENT(加工管理标准)-(三)完结
10 消息服务详情 10.1 本章定义实现加工管理概念所需的消息服务。这些消息已在第8.1节中初步介绍。 协议无关性:这些服务独立于所使用的消息协议,可映射至SECS-II(SEMI E5)或其他类似协议。 10.1.1 消息服务定义内容包括&#…...
算法竞赛进阶指南.次小生成树
目录 题目算法标签: K r u s k a l Kruskal Kruskal, M S T MST MST, 倍增优化, l c a lca lca思路代码*警示后人 题目 356. 次小生成树 算法标签: K r u s k a l Kruskal Kruskal, M S T MST MST, 倍增优化, l c a lca lca 思路 因为要求的是严格次小生成树, 假设最…...
ElasticSearch基本概念
为什么要使用ElasticSearch Elasticsearch 主要为系统提供搜索功能, MySQL 这类传统关系型数据库主要为系统提供数据存储功能 Elasticsearch 的优势 : 支持多种数据类型,非结构化,数值,地理信息。简单的 RESTful AP…...
普通IT的股票交易成长史--20250508晚复盘
声明:本文章的内容只是自己学习的总结,不构成投资建议。价格行为理论学习可参考简介中的几位,感谢他们的无私奉献。 送给自己的话: 仓位就是生命,绝对不能满仓!!!!&…...
SAP 交货单行项目含税金额计算报cx_sy_zerodivide处理
业务背景:SAP交货单只有数量,没有金额,所以开发报表从订单的价格按数量计算交货单的金额。 用户反馈近期报表出现异常: ****2012/12/12 清风雅雨 规格变更 Chg 修改开始 ** 修改原因:由于余数为0时,可能会报错溢出。…...
基于译码器和锁存器的运行逻辑的简易算法
74HC138 def decoder_74hc138(E1, E2, E3, A0, A1, A2):output [1] * 8 # 默认全高电平# 检查使能条件:E1和E2低电平,E3高电平if E1 0 and E2 0 and E3 1:# 计算地址索引(A2为高位,A0为低位)index (A2 <<…...
用电信息采集中的天线种类
一、4G/3G/2G 频率范围“698-960/1710-2700MHz 输入阻抗:50Ω 电压驻波比:<3.0 增益:5dBi/7dBi/9dBi; 824MHz~960MHz频段本体增益≥3.0dBi 1710MHz~2700MHz频段本体增益≥5.0dBi 天线长度225*30mm…...
2025年4月AI算力领域热点事件全景报告
目录 一、政策要闻 01欧洲央行召开会议讨论AI影响 02中国生成式AI备案制落地 03多国政府公布AI基础设施投资计划 04香港发布生成式AI技术及应用指引 05美国出口管制政策影响 06欧盟《人工智能法案》落地 07中国 “东数西算” 工程深化 08美国CHIPS法案争议 09中国发…...