pdfjs 实现给定pdf数据切片高亮并且跳转

pdfjs 实现给定pdf数据切片高亮并且跳转

  • pdfjs 类的改写
    • 基本展示需求的实现
    • 高亮功能的实现
      • 查询功能分析
      • 切片数据处理

pdfjs 类的改写

需求: pdf文件被解析成多个分段,每个分段需要能够展示,并且通过点击分段实现源pdf内容的高亮以及跳转需求。

pdfjs 中文文档
https://gitcode.gitcode.host/docs-cn/pdf.js-docs-cn/getting_started/index.html
https://github.com/mozilla/pdf.js
文档不够详细。pdf难就难在文档上

基本展示需求的实现

pdf.js 是一个由 Mozilla 开发的 JavaScript 库,可以在 Web 浏览器中显示 PDF 文档。pdf.js 将 PDF 文档转换为 HTML5 Canvas 元素,并使用 JavaScript 控制文档的呈现和交互。pdf.js 使得不需要在计算机上安装 Adobe Reader 或其他 PDF 阅读器就可以在 Web 上阅读 PDF 文档成为可能。pdf.js是一个免费的开源软件,使用和修改都非常方便。

pdf.js / src 是pdf.js 的 api层

pdf.js / web 是显示层,在api层的基础上进行UI展示,包括:pdf分页懒加载、切换页码、缩放、查找文字、选择本地文件、侧边栏导航、打印等功能。

预构建版本
├── build/
│   ├── pdf.js                             - display layer
│   ├── pdf.js.map                         - display layer's source map
│   ├── pdf.worker.js                      - core layer
│   └── pdf.worker.js.map                  - core layer's source map
├── web/
│   ├── cmaps/                             - character maps (required by core)
│   ├── compressed.tracemonkey-pldi-09.pdf - PDF file for testing purposes
│   ├── debugger.js                        - helpful debugging features
│   ├── images/                            - images for the viewer and annotation icons
│   ├── locale/                            - translation files
│   ├── viewer.css                         - viewer style sheet
│   ├── viewer.html                        - viewer layout
│   ├── viewer.js                          - viewer layer
│   └── viewer.js.map                      - viewer layer's source map
└── LICENSE源码版本
├── docs/                                  - website source code
├── examples/                              - simple usage examples
├── extensions/                            - browser extension source code
├── external/                              - third party code
├── l10n/                                  - translation files
├── src/
│   ├── core/                              - core layer
│   ├── display/                           - display layer
│   ├── shared/                            - shared code between the core and display layers
│   ├── interfaces.js                      - interface definitions for the core/display layers
│   ├── pdf.*.js                           - wrapper files for bundling
│   └── worker_loader.js                   - used for developer builds to load worker files
├── test/                                  - unit, font and reference tests
├── web/                                   - viewer layer
├── LICENSE
├── README.md
├── gulpfile.js                            - build scripts/logic
├── package-lock.json                      - pinned dependency versions
└── package.json                           - package definition and dependencies

展示功能很多人都做过了。我就不写了,粘一篇文章
pdf.js使用全教程

高亮功能的实现

由于后端的切片内容和前端从pdfjs中拿到的切片内容应该是相同的。即 ai \n mind\n 是 一款\n之类的切片。所以我们可以用后端切片去匹配我们前端的切片。(通过数据长度以即每个片的index来判断),pdfjs也是通过这样的方式来实现的高亮。

  1. 切片数据格式: [切片1,切片2](注意)
  2. 移除其他功能(直接在pdfview.html 文件中添加hidden类名实现隐藏)
  3. 切片渲染可参考pdf的查找功能,通过切片数据与pdf.js 解析出的文本数据 计算出数据,该数据结构与查找高亮的数据结构保持一致,通过pdfjs原生的渲染功能来进行渲染;
  4. 切片定位可参考pdf的查找功能。

查询功能分析

findController.pageMatches:第n页匹配到,相对于本页文本数据的开始index

findController.pageMatchesLenght:第n页匹配到的匹配字符串的长度
在这里插入图片描述

this._convertMatches 方法处理后的数据

在这里插入图片描述

粘贴 updateMatches方法

_updateMatches(reset = false) {  // 清空原高亮筛选逻辑,调用_renderMatches渲染新高亮样式if (!this.enabled && !reset) {return;}const {findController,matches,pageIdx} = this;const {textContentItemsStr,textDivs} = this;let clearedUntilDivIdx = -1;for (const match of matches) {const begin = Math.max(clearedUntilDivIdx, match.begin.divIdx);for (let n = begin, end = match.end.divIdx; n <= end; n++) {const div = textDivs[n];div.textContent = textContentItemsStr[n];div.className = "";}clearedUntilDivIdx = match.end.divIdx + 1;}if (!findController?.highlightMatches || reset) {return;}console.log('findController.pageMatches 第n页匹配到,相对于本页文本数据的开始index',findController.pageMatches)console.log('findController.pageMatchesLength 第n页匹配到的匹配字符串的长度', findController.pageMatchesLength)const pageMatches = findController.pageMatches[pageIdx] || null;console.log('pageMatches', pageMatches);const pageMatchesLength = findController.pageMatchesLength[pageIdx] || null;this.matches = this._convertMatches(pageMatches, pageMatchesLength); console.log('this.matches', this.matches)this._renderMatches(this.matches);}
}

text_highlighter 类中处理逻辑

enable:页面渲染时调用,初始化绑定事件,调用页面渲染更新

_convertMatches:将数据1转换成数据2

_updateMatches:清空原高亮样式,调用_renderMatches渲染新高亮样式

_renderMatches:渲染高亮,并调用findController.scrollMatchIntoView

滚动到指定位置

切片数据处理

数据结构说明
切片数据: 必须有分页信息不然无法匹配每页的textHeight实例
[// 第一页切片{pageIndex: 0cutInfo: ['内容1','内容2'.....]} // 第二页切片{pageIndex: 1cutInfo: ['内容1','内容2'.....]} ]

注意事项:

1、normalizeUnicode处理文本数据,如 fi 这是一个字符,前后端解析可能会不一致,将前后端解析出来的数据使用pdf.js api暴露出来的normalizeUnicode进行处理,处理后为i f,两个字符,

2、空白字符过滤:前后端数据可能会存在空格、换行等空白字符差异(存在什么样的差异,比如前端会将多个空格合并成1个空格),计算时需要过滤。(我处理的正则/\s|\u0000|./g)

3、后端的切片数据 与 前端pdf拿到的数据有出入,渲染时无法完全对应

切片数据处理

1、将切片数据处理成分页的数据2,命名为pagesMatches,并添加自定义标识,

在这里插入图片描述
2、在text_highlighter中注册事件updatePagesMatches,用于接收存储pagesMatches,并调用_updateMatches重新渲染。

3、改造text_highlighter中_updateMatches,1清空原高亮出代码,2将第n页pagesMatches与查询的数据2合并,生成新的有自定义标识的数据2,使高亮渲染切片后查询功能正常。

4、调用_renderMatches进行渲染,将扩展的字段添加到html元素中,并添加样式。高亮渲染完成了。

在这里插入图片描述
在这里插入图片描述
5、高亮定位:

pagesMatches数据添加扩展字段isSelected
在这里插入图片描述
使所在页码滚动到可视区域PDFViewerApplication.pdfViewer.currentPageNumber=n

在_renderMatches渲染时根据 isSelected 与搜索选中selected 判断获取应该滚动到的html元素

调用findController.scrollMatchIntoView进行滚动

// 粘贴 textheight类
class TextHighlighter {constructor({findController,eventBus,pageIndex}) {this.findController = findController;this.matches = [];this.eventBus = eventBus;this.pageIdx = pageIndex;this._onUpdateTextLayerMatches = null;this.textDivs = null;this.textContentItemsStr = null;this.enabled = false;// 没有则创建 _onUpdatePagesMatchesif (!this._onUpdatePagesMatches) {this._onUpdatePagesMatches = (evt) => {if (evt.pagesMatches !== defaultPagesMatches) {defaultPagesMatches = evt.pagesMatches;defaultPagesMatchesIsFocus = true;sessionStorage.removeItem("pdfFindBar");}this._updateMatches(false);};this.eventBus._on("updatePagesMatches", this._onUpdatePagesMatches);}}setTextMapping(divs, texts) {this.textDivs = divs;this.textContentItemsStr = texts;}enable() { // 页面渲染时调用,初始化绑定事件,调用页面渲染更新console.log('enable')if (!this.textDivs || !this.textContentItemsStr) {throw new Error("Text divs and strings have not been set.");}if (this.enabled) {throw new Error("TextHighlighter is already enabled.");}console.log('页面渲染------------');this.enabled = true;if (!this._onUpdateTextLayerMatches) {this._onUpdateTextLayerMatches = evt => {if (evt.pageIndex === this.pageIdx || evt.pageIndex === -1) {this._updateMatches();}};this.eventBus._on("updatetextlayermatches", this._onUpdateTextLayerMatches);}if (!this._onUpdatePagesMatches) {this._onUpdatePagesMatches = (evt) => {if (evt.pagesMatches !== defaultPagesMatches) {defaultPagesMatches = evt.pagesMatches;defaultPagesMatchesIsFocus = true;sessionStorage.removeItem("pdfFindBar");}this._updateMatches(false);};this.eventBus._on("updatePagesMatches", this._onUpdatePagesMatches);}this._updateMatches();}disable() {if (!this.enabled) {return;}console.log('disable')this.enabled = false;if (this._onUpdateTextLayerMatches) {this.eventBus._off("updatetextlayermatches", this._onUpdateTextLayerMatches);this._onUpdateTextLayerMatches = null;}// disable时候移除监听方法if (this._onUpdatePagesMatches) {this.eventBus._off("updatePagesMatches", this._onUpdatePagesMatches);this._onUpdatePagesMatches = null;}this._updateMatches(true);}_convertMatches(matches, matchesLength) { // _convertMatches:将数据转换成begin end 格式if (!matches) {return [];}const {textContentItemsStr} = this;let i = 0let iIndex = 0;const end = textContentItemsStr.length - 1;const result = [];try {for (let m = 0, mm = matches.length; m < mm; m++) {let matchIdx = matches[m];while (i !== end && matchIdx >= iIndex + textContentItemsStr[i].length) {iIndex += textContentItemsStr[i].length;i++;}if (i === textContentItemsStr.length) {console.error("Could not find a matching mapping");}const match = {begin: {divIdx: i,offset: matchIdx - iIndex}};matchIdx += matchesLength[m];while (i !== end && matchIdx > iIndex + textContentItemsStr[i].length) {iIndex += textContentItemsStr[i].length;i++;}match.end = {divIdx: i,offset: matchIdx - iIndex};result.push(match);}} catch {debuggerconsole.log(2222222222);}debuggerreturn result;}_renderMatches(matches) {// Early exit if there is nothing to render.if (matches.length === 0) {return;}const isPagesMatch = sessionStorage.getItem("pdfFindBar") !== "pdfFindBar";const { textContentItemsStr, textDivs, findController, pageIdx } = this;if (!textDivs?.length) {return;}const isSelectedPage = findController?.selected? pageIdx === findController.selected.pageIdx: true;const selectedMatchIdx = findController?.selected?.matchIdx ?? 0;// const highlightAll = !options ? findController.state.highlightAll : true;const highlightAll = true;let prevEnd = null;const infinity = {divIdx: -1,offset: undefined,};function beginText(begin, className, styles) {const divIdx = begin.divIdx;if (!textDivs[divIdx]) {return;}textDivs[divIdx].textContent = "";return appendTextToDiv(divIdx, 0, begin.offset, className, styles);}function appendTextToDiv(divIdx, fromOffset, toOffset, className, styles) {let div = textDivs[divIdx];if (!div) {return;}if (div.nodeType === Node.TEXT_NODE) {const span = document.createElement("span");div.before(span);span.append(div);textDivs[divIdx] = span;div = span;}const content = textContentItemsStr[divIdx].substring(fromOffset,toOffset);const node = document.createTextNode(content);if (className) {const span = document.createElement("span");if (styles && span) {for (let p in styles) {span.style[p] = styles[p];}}span.className = `${className} appended`;span.append(node);div.append(span);return className.includes("selected") ? span.offsetLeft : 0;}div.append(node);return 0;}let i0 = selectedMatchIdx,i1 = i0 + 1;if (highlightAll) {i0 = 0;i1 = matches.length;} else if (!isSelectedPage) {// Not highlighting all and this isn't the selected page, so do nothing.return;}let lastDivIdx = -1;let lastOffset = -1;let selectedElement;let findIndex = -1;for (let i = i0; i < i1; i++) {const match = matches[i];const begin = match.begin;if (begin.divIdx === lastDivIdx && begin.offset === lastOffset) {// It's possible to be in this situation if we searched for a 'f' and we// have a ligature 'ff' in the text. The 'ff' has to be highlighted two// times.continue;}lastDivIdx = begin.divIdx;lastOffset = begin.offset;const end = match.end;if (match.sectionIndex === undefined) {findIndex += 1;}const isSelected = isPagesMatch? match.isSelected: isSelectedPage && findIndex === selectedMatchIdx;const highlightSuffix = " " + match.className;let selectedLeft = 0;// Match inside new div.if (!prevEnd || begin.divIdx !== prevEnd.divIdx) {// If there was a previous div, then add the text at the end.if (prevEnd !== null) {appendTextToDiv(prevEnd.divIdx, prevEnd.offset, infinity.offset);}// Clear the divs and set the content until the starting point.beginText(begin);} else {appendTextToDiv(prevEnd.divIdx, prevEnd.offset, begin.offset);}if (begin.divIdx === end.divIdx) {selectedLeft = appendTextToDiv(begin.divIdx,begin.offset,end.offset,"highlight" + highlightSuffix,match.styles);} else {selectedLeft = appendTextToDiv(begin.divIdx,begin.offset,infinity.offset,"highlight begin" + highlightSuffix,match.styles);for (let n0 = begin.divIdx + 1, n1 = end.divIdx; n0 < n1; n0++) {if (textDivs[n0]) {if (match.styles) {for (let p in match.styles) {textDivs[n0].style[p] = match.styles[p];}}textDivs[n0].className = "highlight middle" + highlightSuffix;}}beginText(end, "highlight end" + highlightSuffix, match.styles);}prevEnd = end;if (!selectedElement && isSelected) {let divIdx = begin.divIdx;while (!textContentItemsStr[divIdx] && divIdx <= end.divIdx) {divIdx++;}const div = textDivs[divIdx];let isOut = false;// 定位元素需要在可视区域内try{const divStyle = div.style;let textLayerNode = div;while (!textLayerNode.classList.contains("textLayer") &&textLayerNode.parentElement) {textLayerNode = textLayerNode.parentElement;}let left = parseFloat(divStyle.left.match(/\d+/g)[0] || "0");let top = parseFloat(divStyle.top.match(/\d+/g)[0] || "0");if ((divStyle.left.includes("%") && left >= 100) ||(divStyle.top.includes("%") && top >= 100)) {isOut = true;}if (textLayerNode.classList.contains("textLayer")) {let width = parseFloat(textLayerNode.style.width.match(/\d+/g)[0] || "0");let height = parseFloat(textLayerNode.style.height.match(/\d+/g)[0] || "0");if ((divStyle.left.includes("px") && left > width) ||(!divStyle.top.includes("px") && top > height)) {isOut = true;}}} catch(e) {console.error(e)}if (!isOut && defaultPagesMatchesIsFocus && isPagesMatch) {selectedElement = div;} else if (!isOut && !isPagesMatch) {selectedElement = div;}}}if (selectedElement && findController) {findController.scrollMatchIntoView({element: selectedElement,selectedLeft: 0,pageIndex: pageIdx,matchIndex: selectedMatchIdx,});defaultPagesMatchesIsFocus = false;}if (prevEnd) {appendTextToDiv(prevEnd.divIdx, prevEnd.offset, infinity.offset);}}/*** * @desc 合并数据方法 * @returns */_merageMatches(baseMatchs, matches) {while (matches.length) {const match = matches[0];const beginIndex = baseMatchs.findIndex((item) => {return ((item.begin.divIdx < match.begin.divIdx ||(item.begin.divIdx === match.begin.divIdx &&item.begin.offset <= match.begin.offset)) &&(item.end.divIdx > match.begin.divIdx ||(item.end.divIdx === match.begin.divIdx &&item.end.offset > match.begin.offset)));});const endIndex = baseMatchs.findIndex((item) => {return ((item.begin.divIdx < match.end.divIdx ||(item.begin.divIdx === match.end.divIdx &&item.begin.offset <= match.end.offset)) &&(item.end.divIdx > match.end.divIdx ||(item.end.divIdx === match.end.divIdx &&item.end.offset >= match.end.offset)));});if (endIndex === -1 && beginIndex === -1) {baseMatchs.push({...match,});matches.shift();continue;}if (endIndex !== 1 && beginIndex !== -1 && endIndex !== beginIndex) {baseMatchs[beginIndex].end = { ...match.begin };baseMatchs[endIndex].begin = { ...match.end };baseMatchs.splice(beginIndex + 1, endIndex - beginIndex - 1, match);matches.shift();continue;}if (endIndex !== -1 && beginIndex !== -1 && endIndex === beginIndex) {baseMatchs.splice(beginIndex,1,{...baseMatchs[beginIndex],end: { ...match.begin },},match,{...baseMatchs[beginIndex],begin: { ...match.end },});matches.shift();continue;}if (endIndex !== -1 && beginIndex === -1) {baseMatchs[endIndex].begin = { ...match.end };baseMatchs.splice(0, 0, match);matches.shift();continue;}if (endIndex === -1 && beginIndex !== -1) {baseMatchs[beginIndex].end = { ...match.begin };baseMatchs.push(match);matches.shift();continue;}matches.shift();console.log("没有处理的", endIndex, beginIndex);}baseMatchs.sort((a, b) => {return a.begin.divIdx - b.begin.divIdx;});return baseMatchs.filter((item) => {if (item.begin.divIdx === item.end.divIdx && item.begin.offset === item.end.offset) {return false}return true})}_updateMatches(reset = false) {  // 清空原高亮筛选逻辑,调用_renderMatches渲染新高亮样式if (!this.enabled && !reset) {return;}// this.pageIdx   当前页数indexconst { findController, pageIdx } = this;const { textContentItemsStr, textDivs, matches = [] } = this;// console.log('findController', findController);// console.log('pageIdx', pageIdx);// console.log('textContentItemsStr', textContentItemsStr);// console.log('textDivs', textDivs);// console.log('matches', matches);// 清楚匹配项for (let i = 0, ii = matches.length; i < ii; i++) {const match = matches[i];const begin = match.begin.divIdx;for (let n = begin, end = match.end.divIdx; n <= end; n++) {const div = textDivs[n];div.textContent = textContentItemsStr[n];div.className = "";}}// console.log('defaultPagesMatches',defaultPagesMatches);let sectionMatches = [...(defaultPagesMatches?.[this.pageIdx] || [])];if (findController?.highlightMatches && !reset) {const pageMatches = findController.pageMatches[pageIdx] || null;const pageMatchesLength =findController.pageMatchesLength[pageIdx] || null;const findMatches = this._convertMatches(pageMatches, pageMatchesLength);console.log('findMatches', findMatches);const selectedMatchIdx = findController.selected.matchIdx;pageIdx === findController.selected.pageIdx &&findMatches[selectedMatchIdx] &&(findMatches[selectedMatchIdx].className = "selected");this.matches = this._merageMatches(sectionMatches, findMatches);console.log('this.matches', this.matches);this._renderMatches(this.matches || []);return;}console.log('2222sectionMatches', sectionMatches);this.matches = sectionMatches;this._renderMatches(sectionMatches || []);// const {//   findController,//   matches,//   pageIdx// } = this;// const {//   textContentItemsStr,//   textDivs// } = this;// let clearedUntilDivIdx = -1;// for (const match of matches) {//   const begin = Math.max(clearedUntilDivIdx, match.begin.divIdx);//   for (let n = begin, end = match.end.divIdx; n <= end; n++) {//     const div = textDivs[n];//     div.textContent = textContentItemsStr[n];//     div.className = "";//   }//   clearedUntilDivIdx = match.end.divIdx + 1;// }// if (!findController?.highlightMatches || reset) {//   return;// }// console.log('findController.pageMatches 第n页匹配到,相对于本页文本数据的开始index',findController.pageMatches)// console.log('findController.pageMatchesLength 第n页匹配到的匹配字符串的长度', findController.pageMatchesLength)// const pageMatches = findController.pageMatches[pageIdx] || null;// console.log('pageMatches', pageMatches);// const pageMatchesLength = findController.pageMatchesLength[pageIdx] || null;// this.matches = this._convertMatches(pageMatches, pageMatchesLength); // console.log('this.matches', this.matches)// this._renderMatches(this.matches);}
}
调用: 
function handleTest(evt: any) {let str = 'AiMind\n文档库'let pdfJs = document.getElementsByTagName('iframe')[0]let PDFViewerApplication = (window[0] as any).PDFViewerApplicationPDFViewerApplication.pageIndex = 1let update = PDFViewerApplication.eventBuslet normalizeUnicode = (window[0] as any).pdfjsLib.normalizeUnicodelet unicodeHandledStr = normalizeUnicode(str)const regex = /\s|\u0000|\./g;let regHandledStr = unicodeHandledStr.replace(regex, ' ')let metchShotStr = regHandledStr.split(' ')// 写死数据测试let testData = {0: [{sectionIndex: 0,className: 'section-0 section-color-0',begin: {divIdx: 0,offset: 0}, end: {divIdx: 0, offset: 2}},{sectionIndex: 1,className: 'section-1 section-color-0',begin: {divIdx: 3,offset: 0}, end: {divIdx: 3, offset: 50}},]}// 通知pdfjs 高亮渲染update.dispatch('updatePagesMatches', { pagesMatches:testData })}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/772899.shtml

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

相关文章

视频声音生成字幕 pr生成视频字幕 以及字幕乱码的解决

目录 目录 1、首先把要生成字幕的视频拖入以创建序列 2、点击工具栏的 窗口 选择 文本 3、选择字幕下的 转录序列 4、选择输出的语言&#xff08;主要看视频声音说的是啥语言&#xff09; 5、音轨 选择 音频1​编辑 6、点击转录 7、等待转录文本 8、点击创建说明性字幕按…

论文阅读笔记——Rethinking Pointer Reasoning in Symbolic Execution

文章目录 前言Rethinking Pointer Reasoning in Symbolic Execution12.1、基本情况概述12.2、摘要12.3、引言12.4、方法12.4.1、基本版本12.4.1.1、内存加载和存储12.4.1.2、状态合并 12.4.2、改进12.4.2.1、地址范围选择12.4.2.2、内存清理12.4.2.3、符号化的未初始化内存12.4…

卷起来——高级数据分析师

要成为一名高级数据分析师&#xff0c;需要掌握一系列的技能&#xff0c;包括数据处理、统计分析、机器学习、数据可视化以及业务理解等&#xff0c;喜欢或者想往这方面发展的童鞋们&#xff0c;卷起来&#xff0c;点击以下链接中的链接&#xff0c;备注"分析"进群交…

Clip Converter - 视频在线下载方法

Clip Converter - 视频在线下载方法 1. Video URL to Download2. Continue3. StartReferences YT to MP4 & MP3 Converter! https://www.clipconverter.cc/ Clip Converter is a free online media conversion application, which allows you to reocord, convert and do…

YOLOv8改进 | 主干篇 | 修复官方去除掉PP-HGNetV2的通道缩放功能(轻量又涨点,全网独家整理)

一、本文介绍 本文给大家带来的改进机制是大家在跑RT-DETR提供的HGNetV2时的一个通道缩放功能&#xff08;官方在前几个版本去除掉的一个功能&#xff09;&#xff0c;其中HGNetV2当我们将其集成在YOLOv8n的模型上作为特征提取主干的时候参数量仅为230W 计算量为6.7GFLOPs该网…

无人直播(视频推流)

环境搭建 我这里采用的是ffmpeg来进行推流直播 yum -y install wgetwget --no-check-certificate https://www.johnvansickle.com/ffmpeg/old-releases/ffmpeg-4.0.3-64bit-static.tar.xztar -xJf ffmpeg-4.0.3-64bit-static.tar.xzcd ffmpeg-4.0.3-64bit-staticmv ffmpeg /u…

kubernetes-networkpolicies网络策略问题

kubernetes-networkpolicies网络策略问题 问题描述 重点重点重点&#xff0c;查看我的博客CKA考题&#xff0c;里面能找到解决方法 1.部署prometheus监控的时候&#xff0c;都部署成功&#xff0c;但是web访问503-504超时 2.添加ingress的时候也是访问不到&#xff0c;其他命…

寻找最大值最小值

Problem Finding both the minimum and maximum in an array of integers A[1..n] and assume for simplicity that n is a power of 2 A straightforward algorithm 1. x←A[1]; y←A[1] 2. for i←2 to n 3. if A[i] < x then x←A[i] 4. if A[i] > y then y←A[i…

gin语言基础学习--会话控制(下)

练习 模拟实现权限验证中间件 有2个路由&#xff0c;/cookie和/home/cookie用于设置cookiehome是访问查看信息的请求在请求home之前&#xff0c;先跑中间件代码&#xff0c;检验是否存在cookie 访问home&#xff0c;会显示错误&#xff0c;因为权限校验未通过 package mainim…

centos创建svn库步骤

1.切换root用户 1、设置root用户的密码&#xff1a; sudo passwd root 2、切换到root用户权限 su 3、切换回个人用户权限 exit 2.用root用户执行yum install -y subversion 3.创建文件夹mkdir -p /data/svn/repository 4.创建SVN 版本库 5.输入命令&#xff1a; svnadmin creat…

IDEA连接github.com连接超时 Invalid authentication data. connect time out 的问题解决(亲测有效)

问题&#xff1a; IDEA连接github.com连接超时 Invalid authentication data. connect time out 解决方案&#xff08;亲测有效&#xff09;&#xff1a; 修改host文件&#xff1a;打开 C:\Windows\System32\drivers\etc\hosts&#xff0c;文件末尾添加如下内容&#xff1a; …

OriginBot智能机器人开源套件

详情可参见&#xff1a;OriginBot智能机器人开源套件——支持ROS2/TogetherROS&#xff0c;算力强劲&#xff0c;配套古月居定制课程 (guyuehome.com) OriginBot智能机器人开源套件 最新消息&#xff1a;OriginBot V2.1.0版本正式发布&#xff0c;新增车牌识别&#xff0c;点击…

Vue3基础笔记(2)事件

一.事件处理 1.内联事件处理器 <button v-on:click"count">count1</button> 直接将事件以表达式的方式书写~ 每次单击可以完成自增1的操作~ 2.方法事件处理器 <button click"addcount(啦啦啦~)">count2</button> 如上&…

VMware下建立CentOS 7

1.点击新建虚拟机 2.下一步 3.选择号安装程序光盘映像文件位置&#xff0c;下一步 4.选择版本和操作系统然后下一步 5.编辑虚拟机名称并选择安装位置&#xff0c;然后下一步 6.设置最大磁盘大小&#xff0c;下一步 7.点击完成 8.点击编辑虚拟机设置 9.将此虚拟机内存设置为2G&a…

中间件学习--InfluxDB部署(docker)及springboot代码集成实例

一、需要了解的概念 1、时序数据 时序数据是以时间为维度的一组数据。如温度随着时间变化趋势图&#xff0c;CPU随着时间的使用占比图等等。通常使用曲线图、柱状图等形式去展现时序数据&#xff0c;也就是我们常常听到的“数据可视化”。 2、时序数据库 非关系型数据库&#…

爬虫实践(1)

这一篇只提登录模拟&#xff0c;主要介绍chrome开发者窗口的使用&#xff0c;实际上相关接口调用都是用到cookie&#xff0c;需要再加一篇从token到cookie&#xff0c;以保证实践的完整性 以migu登录为例&#xff0c;分析其登录过程&#xff0c;之后可以使用任意语言模拟登录&…

小程序富文本图片宽度自适应

解决这个问题 创建一个util.js文件,图片的最大宽度设置为100%就行了 function formatRichText(html) {let newContent html.replace(/\<img/gi, <img style"max-width:100%;height:auto;display:block;");return newContent; }module.exports {formatRichT…

vue2创建项目(自用,初学)

vue2创建项目(自用&#xff0c;初学) 创建项目 1.在文件资源管理器中&#xff0c;选择想建立文件夹的目录&#xff0c;输入cmd指令 vue create 项目名2.初学练习选择最后一项 3.按空格进行勾选&#xff0c;回车下一步 4.因为是vue2&#xff0c;所以选2.x 5.选y 6.选Less 7.选…

canvas跟随鼠标画有透明度的椭圆边框

提示&#xff1a;canvas跟随鼠标画有透明度的椭圆边框 文章目录 前言一、跟随鼠标画有透明度的椭圆边框总结 前言 一、跟随鼠标画有透明度的椭圆边框 test.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8">&…

论文阅读-《Lite Pose: Efficient Architecture Design for 2D Human Pose Estimation》

摘要 这篇论文主要研究了2D人体姿态估计的高效架构设计。姿态估计在以人为中心的视觉应用中发挥着关键作用&#xff0c;但由于基于HRNet的先进姿态估计模型计算成本高昂&#xff08;每帧超过150 GMACs&#xff09;&#xff0c;难以在资源受限的边缘设备上部署。因此&#xff0…