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

前端渲染pdf文件解决方案-pdf.js

目录

一、前言

二、简介

1、pdf.js介绍

2、插件版本参数

三、通过viewer.html实现预览(推荐)

1、介绍

2、部署

【1】下载插件包

【2】客户端方式

【3】服务端方式(待验证)

3、使用方法

【1】预览PDF文件

【2】外部搜索条件触发pdf.js的搜索逻辑

四、把pdf渲染为canvas实现预览

1、安装

2、功能实现

【1】实现pdf预览

【2】实现pdf内容文本可选进行复制

【3】实现搜索,匹配内容高亮,并且可以跳转至匹配内容的位置

【4】获取pdf文件中目录的数据结构


一、前言

        在前端开发中,渲染PDF文件一直是一项重要而挑战性的任务。而今,我们可以借助pdf.js库来轻松实现这一目标。pdf.js是一个开源的JavaScript库,它可以在浏览器中渲染PDF文件,实现了在网页上查看PDF文档的功能。它提供了丰富的API和功能,使得在前端页面展示PDF文件变得轻而易举。让我们一起探索pdf.js的奇妙之处,轻松实现前端PDF文件的渲染与展示吧!

二、简介

1、pdf.js介绍

        pdf.js是一款基于JavaScript的开源PDF阅读器组件,可以在网页中直接显示和操作PDF文件,目前已知的前端渲染pdf组件都是基于pdf.js进行封装。

git地址:https://github.com/mozilla/pdf.js

注:开源且免费

2、插件版本参数

插件版本
Nodev22.13.0

@types/react

^18.0.33

@types/react-dom

^18.0.11
pdfjs-2.5.207-es5-dist.zip (viewer.js使用方式)2.5.207
pdfjs-dist   (canvas渲染方式)

3.6.172

三、通过viewer.html实现预览(推荐)

1、介绍

        除了PDF预览,还待配套的工具栏,支持功搜索、缩放、目录、打印等功能~ 

Demo如图:

2、部署

【1】下载插件包

下载地址:https://github.com/mozilla/pdf.js/releases/tag/v2.5.207

【2】客户端方式

       把下载后的pdfjs-2.5.207-es5-dist.zip解压后,放在项目中的public文件夹下

【3】服务端方式(待验证)

   可将pdf.js包 放到服务器上 如:http://[ip]:[port]/static/pdfjs

3、使用方法

【1】预览PDF文件

        1)客户端方式(基于React框架为例)

const viewPDF: React.FC = () => {// pdf文件路径,放在项目的public目录下const pdfUrl = '/A.pdf'; //pdf.js库的代码,放在项目的public目录下const pdfServerUrl = '/pdfjs-2.5.207-es5-dist/web/viewer.html'const url = `${pdfServerUrl}?file=${pdfUrl}`return <><h1>pdf 搜索(基于pdf-dist,pdf_viewer.html)</h1><iframe id='pdfIframe' src={url} width="100%" height="100%"></iframe></>;
}

        2)服务端方式(待整理)

【2】外部搜索条件触发pdf.js的搜索逻辑

  • 跳转至第一个匹配的内容
  • 匹配内容高亮
const viewPDF: React.FC = () => {// pdf文件路径,放在项目的public目录下const pdfUrl = '/A.pdf'; //pdf.js库的代码,放在项目的public目录下const pdfServerUrl = '/pdfjs-2.5.207-es5-dist/web/viewer.html'const url = `${pdfServerUrl}?file=${pdfUrl}`let pdfContentWindow: any = null; //缓存iframContentconst getPdfContent = () => {const pdfFrame: any = document.getElementById('pdfIframe');if (!pdfFrame) {return;}pdfContentWindow = pdfFrame.contentWindow;//pdf组件部分信息,包括:当前页码、总共页码等console.log('page===>', pdfContentWindow.PDFViewerApplication); }const onSearchForOut = (searchText: string) => {pdfContentWindow.postMessage(searchText, '*');pdfContentWindow.addEventListener('message', (e: any) => {// 高亮匹配结果pdfContentWindow.PDFViewerApplication.findBar.findField.value = e.data;pdfContentWindow.PDFViewerApplication.findBar.highlightAll.checked = true;                        pdfContentWindow.PDFViewerApplication.findBar.dispatchEvent('highlightallchange');//触发搜索项‘下一个’事件pdfContentWindow.PDFViewerApplication.findBar.dispatchEvent('again', false);}, false);}useEffect(() => {getPdfContent();setTimeout(() => {// 外部的搜索条件onSearchForOut('阳区CBD核心区')}, 3* 1000)}, []);  return <><h1>pdf 搜索(基于pdf-dist,pdf_viewer.html)</h1><iframe id='pdfIframe' src={url} width="100%" height="100%"></iframe></>;
}

四、把pdf渲染为canvas实现预览

1、安装

npm install pdfjs-dist --save

2、功能实现

【1】实现pdf预览

import { Button } from 'antd';
import { useState, useEffect, useRef } from 'react';
import * as pdfjsLib from 'pdfjs-dist'; // 引入pdfjs-dist
const pdfUrl = '/zyk.pdf'; // pdf 文件路径,pdf文件存放于public目录下
const workerUrl = `/pdf.worker.min.js`; //webworker存放于public目录下
pdfjsLib.GlobalWorkerOptions.workerSrc = workerUrl;const viewPdf = (props: {height: string}) => {const {height} = props;const pdfContainerRef = useRef<any>(null);const [pagesList, setPagesList] = useState<any>([]);const scale = 2; // 缩放比例// 渲染单个页面const renderPage = async (page: any, pageNumber: number) => {const viewport = page.getViewport({ scale });const pageContentDom = document.createElement('div');pageContentDom.id = `pdfPage-content-${pageNumber}`;pageContentDom.style.width = `${viewport.width}px`;pageContentDom.style.height = `${viewport.height}px`;pageContentDom.style.position = 'relative';// 创建 Canvas 元素const canvas = document.createElement('canvas');const context = canvas.getContext('2d');canvas.id=`pdfPage-${pageNumber}`canvas.width = viewport.width;canvas.height = viewport.height;canvas.style.border = '1px solid black';pageContentDom.appendChild(canvas);pdfContainerRef.current.appendChild(pageContentDom);// 渲染 PDF 页面到 Canvasawait page.render({canvasContext: context,viewport,}).promise;};// 渲染 PDF 页面const renderPagesGroup = ( pages: any) => {pages.forEach(({page}:any, index: number) => {renderPage(page, index);});};// 加载 PDF 文件const loadPdf = async (url: any) => {const pdf = await pdfjsLib.getDocument(url).promise;const pages: any[] = [];for (let i = 1; i <= pdf.numPages; i++) {const page = await pdf.getPage(i);const textContent = await page.getTextContent();pages.push({page,textContent});}setPagesList(pages);renderPagesGroup(pages);};useEffect(() => {loadPdf(pdfUrl);}, []);return <><div><h1>PDF 搜索(基于@pdfjs-dist-自定义实现)</h1> <div><div style={{ height: height || '500px' }}>{/* PDF 容器 */}<div ref={pdfContainerRef} style={{ position: 'relative', height: '100%', overflowY: 'scroll' }} /></div></div></div></>
};export default viewPdf;

【2】实现pdf内容文本可选进行复制

...
//基于“【1】实现pdf预览”代码, 修改renderPage方法// 渲染单个页面const renderPage = async (page: any, pageNumber: number) => {const viewport = page.getViewport({ scale });const pageContentDom = document.createElement('div');pageContentDom.id = `pdfPage-content-${pageNumber}`;//add-begin: 文本可选则  为了文本层和canvas层重叠,利用组件库的类名(类名不能修改)pageContentDom.className = 'pdfViewer';pageContentDom.style.setProperty('--scale-factor', scale as any);//add-end: 文本可选则pageContentDom.style.width = `${viewport.width}px`;pageContentDom.style.height = `${viewport.height}px`;pageContentDom.style.position = 'relative';// 创建 Canvas 元素const canvas = document.createElement('canvas');const context = canvas.getContext('2d');canvas.id=`pdfPage-${pageNumber}`canvas.width = viewport.width;canvas.height = viewport.height;canvas.style.border = '1px solid black';pageContentDom.appendChild(canvas);createHeightLightCanvas(viewport, pageNumber, pageContentDom);pdfContainerRef.current.appendChild(pageContentDom);// 渲染 PDF 页面到 Canvasawait page.render({canvasContext: context,viewport,}).promise;//add-begin: 文本可选则const textLayerDiv = document.createElement('div');textLayerDiv.style.width = viewport.width;textLayerDiv.style.height = viewport.height;//为了文本层和canvas层重叠,利用组件库的类名textLayerDiv.className = 'textLayer';const textContent = await page.getTextContent();pdfjsLib.renderTextLayer({textContentSource: textContent,container: textLayerDiv,viewport: viewport,textDivs: [],});pageContentDom.appendChild(textLayerDiv);//add-end: 文本可选则};

【3】实现搜索,匹配内容高亮,并且可以跳转至匹配内容的位置

import { Button } from 'antd';
import { useState, useEffect, useRef } from 'react';
import * as pdfjsLib from 'pdfjs-dist'; // 引入pdfjs-dist
const pdfUrl = '/zyk.pdf'; // pdf 文件路径,pdf文件存放于public目录下
const workerUrl = `/pdf.worker.min.js`; //webworker存放于public目录下
pdfjsLib.GlobalWorkerOptions.workerSrc = workerUrl;const viewPdf = (props: {height: string}) => {const {height} = props;const [searchText, setSearchText] = useState('');const pdfContainerRef = useRef<any>(null);const [pagesList, setPagesList] = useState<any>([]);const [matchList, setMatchList] = useState<any>([]);const scale = 2; // 缩放比例const createHeightLightCanvas = (viewport: any, pageNumber: number, parentDom: any) => {// 为每页创建一个高亮层canvasconst highlightCanvas = document.createElement('canvas');highlightCanvas.id = `highlightCanvas-${pageNumber}`;highlightCanvas.className = 'highlightCanvas';highlightCanvas.width = viewport.width;highlightCanvas.height = viewport.height;highlightCanvas.style.position = 'absolute';highlightCanvas.style.top = '0';highlightCanvas.style.left = '0';highlightCanvas.style.zIndex = '1';parentDom.appendChild(highlightCanvas);}// pageNumber 页码(从0开始)const jumpToPage = (pageIndex: number) => {let beforeCanvasHeight = 0;for (let i = 0; i < pageIndex; i++) {const canvasParentDom = pdfContainerRef.current.querySelector(`#pdfPage-content-${i}`);let canvasParentHeight = canvasParentDom.style.height.replace('px', '');beforeCanvasHeight += Number(canvasParentHeight);}pdfContainerRef.current.scrollTo({top: beforeCanvasHeight, // 垂直滚动位置behavior: 'smooth'});}const getCurrentTextContentY = (canvas: any, match: any) => {// pdfjs 坐标系原点在左下角。transform[5]代表y轴的基线,所以需要减去高度const {textBlock} = match;const { transform, height } = textBlock;return canvas.height - (transform[5] + height -2) * scale;}// 滚动到指定的匹配项const scrollToMatch = (match: any) => {const { pageIndex, matchList } = match;const firstMatchContent = matchList[0];// 获取滚动区域的高度const scrollHeight = pdfContainerRef.current.scrollHeight;console.log('滚动区域的高度:', scrollHeight);// 获取当前页码之前dom的高度let beforePageHeight = 0;for (let i = 0; i < pageIndex; i++) {const canvasParentDom = pdfContainerRef.current.querySelector(`#pdfPage-content-${i}`);let canvasParentHeight = canvasParentDom.style.height.replace('px', '');beforePageHeight += Number(canvasParentHeight);}// todo 继续计算 匹配项目的高度const currentPageCanvas = pdfContainerRef.current.querySelector(`#pdfPage-${pageIndex}`);const textContentY = getCurrentTextContentY(currentPageCanvas, firstMatchContent);const offsetTop = 50; //为了滚动目标文字不顶格const targetScrollTop = beforePageHeight + textContentY -offsetTop;pdfContainerRef.current.scrollTo({top: targetScrollTop, // 垂直滚动位置behavior: 'smooth'});};// 绘制高亮区域const drawHighlights = async (canvas: any, matchesList: MatchBlockItem[]) => {if (matchesList.length === 0) {return;}const context = canvas.getContext('2d');context.fillStyle = 'rgba(255, 255, 0, 0.5)'; // 黄色半透明填充matchesList.forEach((match: any) => {const {textBlock} = match;const { transform, width, height, str } = textBlock;// 获取每一个字符的宽度const charWidth = width / str.length;const lightWidth = (match.textEndIndex - match.textStartIndex) * charWidth;const lightHeight = height;const x = transform[4] + match.textStartIndex * charWidth;const y = getCurrentTextContentY(canvas, match);context.fillRect(Math.floor(x * scale), Math.floor(y), Math.ceil(lightWidth * scale), Math.ceil(lightHeight * scale));});};// 渲染单个页面const renderPage = async (page: any, pageNumber: number) => {const viewport = page.getViewport({ scale });const pageContentDom = document.createElement('div');pageContentDom.id = `pdfPage-content-${pageNumber}`;//为了文本层和canvas层重叠,利用组件库的类名pageContentDom.className = 'pdfViewer';pageContentDom.style.setProperty('--scale-factor', scale as any);pageContentDom.style.width = `${viewport.width}px`;pageContentDom.style.height = `${viewport.height}px`;pageContentDom.style.position = 'relative';// 创建 Canvas 元素const canvas = document.createElement('canvas');const context = canvas.getContext('2d');canvas.id=`pdfPage-${pageNumber}`canvas.width = viewport.width;canvas.height = viewport.height;canvas.style.border = '1px solid black';pageContentDom.appendChild(canvas);createHeightLightCanvas(viewport, pageNumber, pageContentDom);pdfContainerRef.current.appendChild(pageContentDom);// 渲染 PDF 页面到 Canvasawait page.render({canvasContext: context,viewport,}).promise;// 渲染文本框const textLayerDiv = document.createElement('div');textLayerDiv.style.width = viewport.width;textLayerDiv.style.height = viewport.height;//为了文本层和canvas层重叠,利用组件库的类名textLayerDiv.className = 'textLayer';const textContent = await page.getTextContent();pdfjsLib.renderTextLayer({textContentSource: textContent,container: textLayerDiv,viewport: viewport,textDivs: [],});pageContentDom.appendChild(textLayerDiv)};// 渲染 PDF 页面const renderPagesGroup = ( pages: any) => {pages.forEach(({page}:any, index: number) => {renderPage(page, index);});};// 加载 PDF 文件const loadPdf = async (url: any) => {const pdf = await pdfjsLib.getDocument(url).promise;const pages: any[] = [];for (let i = 1; i <= pdf.numPages; i++) {const page = await pdf.getPage(i);const textContent = await page.getTextContent();pages.push({page,textContent});}setPagesList(pages);renderPagesGroup(pages);};const findAllMatches = (text: string, pattern: string) => {// 创建正则表达式对象const regex = new RegExp(pattern, 'g');// 使用match方法找到所有匹配项const matches = text.match(regex);// 如果没有匹配项,返回空数组if (!matches) {return [];}// 创建一个数组来存储所有匹配的位置const positions = [];// 遍历所有匹配项,找到它们在字符串中的位置let match;while ((match = regex.exec(text)) !== null) {positions.push(match.index);}return positions;}// todo 优化参数个数,const getMatchesList = (items: any,currentItem: any, currentItemIndex: number,currentTextIndex: number, searchStr: string): MatchBlockItem[] => {let matchSearchList: MatchBlockItem[] = [];if(currentItem.str.length - (currentTextIndex + 1) < searchStr.length -1 ) {// 获取当前文本块中剩余字符,如果小于搜索字符长度,则继续查找下一个文本块let itemText = currentItem.str.slice(currentTextIndex); // 获取当前文本块中剩余字符let tempMatchSearchList = [{blockIndex: currentItemIndex,textStartIndex: currentTextIndex,textEndIndex: currentItem.str.length,// 由于统一使用slice截取,所以不包括最后一位textBlock: currentItem}]; // 存储后续文本块let index = currentItemIndex;const otherSearchLength = searchStr.length -1;while (itemText.length <= otherSearchLength) {index = index + 1;const currentOtherSearchLength = otherSearchLength - itemText.length; // 当前剩余搜索字符长度if (items[index].str.length > currentOtherSearchLength) {// 文本块的长度大于剩余搜索字符长度,则截取剩余搜索字符长度的字符itemText = `${itemText}${items[index].str.slice(0, currentOtherSearchLength+1)}`;tempMatchSearchList.push({blockIndex: index,textStartIndex: 0,textEndIndex: currentOtherSearchLength + 1,textBlock: items[index]})} else {// 文本块的长度小于剩余搜索字符长度,则截取全部字符, 继续itemText = `${itemText}${items[index].str}`;tempMatchSearchList.push({blockIndex: index,textStartIndex: 0,textEndIndex: items[index].str.length,textBlock: items[index]})}}if (itemText === searchStr) {matchSearchList = matchSearchList.concat(tempMatchSearchList);}}else {// 获取当前文本块中剩余字符,如果大于等于搜索字符长度,则截取当前文本块中搜索文本长度的字符const textEndIndex = currentTextIndex + searchStr.length;const text = currentItem.str.slice(currentTextIndex, textEndIndex); // 取出匹配字符所在文本块及后续文本块if (text === searchStr) {console.log('匹配到了:', currentItem, currentItemIndex)matchSearchList.push({blockIndex: currentItemIndex,textStartIndex: currentTextIndex,textEndIndex: textEndIndex,textBlock: currentItem})}}return matchSearchList;}// 查找文本的所有出现位置const findAllOccurrences = (items: any, searchStr: string): MatchBlockItem[] => {const firstSearchStr = searchStr[0];let matchSearchList: MatchBlockItem[] = [];for(let i=0; i<items.length; i++) {const currentItem = items[i];const currentMatchIndexList = findAllMatches(currentItem.str, firstSearchStr); // 获取当前文本块中第一个匹配字符的索引列表if (currentMatchIndexList.length > 0) {for(let j=0; j<currentMatchIndexList.length; j++){matchSearchList = [...matchSearchList, ...getMatchesList(items, currentItem, i, currentMatchIndexList[j], searchStr)];}}}return matchSearchList;};const clearHeightLightsCanvas = () => {const highlightCanvases = Array.from(pdfContainerRef.current.querySelectorAll('.highlightCanvas'));highlightCanvases.forEach((canvas: any) => {const context = canvas.getContext('2d');context.clearRect(0, 0, canvas.width, canvas.height);});}const handleSearch = async () => {clearHeightLightsCanvas()if (!searchText) {jumpToPage(0);return;}const newMatches: any = [];console.log('pagesList', pagesList)// todo  目前是按照每页来匹配,可能会匹配不到跨页的内容pagesList.forEach(async ({textContent}: any, pageIndex: number) => {const pageMatches = findAllOccurrences(textContent.items, searchText);newMatches.push({pageIndex, // 页面索引matchList: pageMatches, // 匹配项列表});})console.log('newMatches', newMatches);const isNotMatch = newMatches.every((match: any) => match.matchList.length === 0);if (isNotMatch) {alert('未找到匹配项');return;}/// 重新绘制高亮区域pagesList.forEach((_: any, pageIndex: number) => {const highlightCanvas = pdfContainerRef.current.querySelectorAll('.highlightCanvas')[pageIndex]; // 获取高亮层 Canvasconst currentMatches = newMatches.find((match: any) => match.pageIndex === pageIndex);drawHighlights(highlightCanvas,currentMatches?.matchList || []);});// 跳转const isExistItem = newMatches.find((match: any) => match.matchList.length > 0);if (isExistItem) {scrollToMatch(isExistItem);}};// 初始化 PDF.jsuseEffect(() => {loadPdf(pdfUrl);}, []);return <><div><h1>PDF 搜索(基于@pdfjs-dist-自定义实现)</h1><inputtype="text"value={searchText}onChange={(e) => setSearchText(e.target.value)}placeholder="输入要搜索的内容"/><Button onClick={handleSearch}>搜索</Button><div><div style={{ height: height || '500px' }}>{/* PDF 容器 */}<div ref={pdfContainerRef} style={{ position: 'relative', height: '100%', overflowY: 'scroll' }} /></div></div></div></>
};export default viewPdf;

【4】获取pdf文件中目录的数据结构

....
//基于‘【1】实现pdf预览’的代码const get= async (url: any) => {const pdf = await pdfjsLib.getDocument(url).promise;// 获取目录数据const pdfCatalogue= await pdf.getOutline();console.log('目录数据:', pdfCatalogue); };
...

相关文章:

前端渲染pdf文件解决方案-pdf.js

目录 一、前言 二、简介 1、pdf.js介绍 2、插件版本参数 三、通过viewer.html实现预览&#xff08;推荐&#xff09; 1、介绍 2、部署 【1】下载插件包 【2】客户端方式 【3】服务端方式&#xff08;待验证&#xff09; 3、使用方法 【1】预览PDF文件 【2】外部搜索…...

接口测试和功能测试详解

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 本文主要分为两个部分&#xff1a; 第一部分&#xff1a;主要从问题出发&#xff0c;引入接口测试的相关内容并与前端测试进行简单对比&#xff0c;总结两者…...

LeetCode热题100--283.移动零--简单

1.题目 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操作。 示例 1: 输入: nums [0,1,0,3,12] 输出: [1,3,12,0,0] 示例 2: 输入: nums [0]…...

CoT 数据集如何让大模型学会「一步一步思考」?

目前&#xff0c;大模型的回答路径基本遵循input-output的方式&#xff0c;在面对复杂任务时表现不佳。反之&#xff0c;人类会遵循一套有条理的思维流程&#xff0c;逐步推理得出正确答案。这种差异促使人们深入思考&#xff1a;如何才能让大模型“智能涌现”&#xff0c;学会…...

量子跃迁:Vue组件安全工程的基因重组与生态免疫(完全体)

总章数字免疫系统的解剖学革命 在2024年某国家级数字政务平台的安全审计中&#xff0c;传统前端架构暴露出的信任链断裂问题&#xff0c;导致公民隐私数据以每秒23TB的速度在暗网流通。当我们用PET扫描技术观察现代Web应用的微观结构&#xff0c;发现94.7%的安全威胁源自组件间…...

配置 Nginx 的 HTTPS

证书文件 文件名 作用 来源 example.com.key 服务器的私钥&#xff0c;用于加密和解密数据。 本地生成 -----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAqp5c... -----END RSA PRIVATE KEY----- example.com.csr 证书签名请求文件&#xff0c;包含公钥和申请者信息&…...

FPGA开发流程初识

FPGA 的开发流程可知&#xff0c;在 FPGA 开发的过程中会产生很多不同功能的文件&#xff0c;为了方便随时查找到对应文件&#xff0c;所以在开始开发设计之前&#xff0c;我们第一个需要考虑的问题是工程内部各种文件的管理。如 果不进行文件分类&#xff0c;而是将所有文件…...

c++中的enum变量 和 constexpr说明符

author: hjjdebug date: 2025年 04月 23日 星期三 13:40:21 CST description: c中的enum变量 和 constexpr说明符 文章目录 1.Q:enum 类型变量可以有,--操作吗&#xff1f;1.1补充: c/c中enum的另一个细微差别. 2.Q: constexpr 修饰的函数,要求传入的参数必需是常量吗&#xff…...

JVM学习笔记

1、jvm概述 1.1、即时编译 为什么java没有c和c快 多了一层解释&#xff0c;而即时编译就是将热点数放入内存&#xff0c;下次执行的时候不用解释&#xff0c;提高了效率 1.2、常见的jvm jvm不止一个&#xff0c;意不意外&#xff1f;惊不惊喜&#xff1f; 1.3、hotspot的发展…...

AtCoder Beginner Contest 402题解

A - CBC 思路&#xff1a;仔细看这题其实就发现&#xff0c;我们只需要遍历一遍字符串把大写字母输出即可&#xff0c;很标准的签到题 #include<bits/stdc.h> using namespace std; #define int long longsigned main() {string s;cin>>s;for(char c:s){if(c>A…...

Rust 语言使用场景分析

1. 引言&#xff1a;Rust 语言概述 Rust 是一门专注于性能、内存安全和并发性的现代系统编程语言。自 2010 年由 Mozilla Research 的 Graydon Hoare 发起&#xff0c;并于 2015 年正式发布以来&#xff0c;Rust 凭借其独特的设计理念和强大的功能集&#xff0c;在技术领域迅速…...

HTTP 请求头的 key 不区分大小写。

详细说明 HTTP 协议规范 根据 RFC 7230&#xff0c;HTTP 头字段的名称&#xff08;即 key&#xff09;在传输时不区分大小写。例如&#xff0c;Content-Type 和 content-type 被视为相同的字段。 实际行为 客户端行为&#xff1a;大多数 HTTP 客户端&#xff08;如浏览器、cur…...

4.RabbitMQ - 延迟消息

RabbitMQ延迟消息 文章目录 RabbitMQ延迟消息一、延迟消息介绍二、实现2.1 死信交换机2.2 延迟消息插件2.3 取消超时订单 一、延迟消息介绍 延迟消息&#xff1a;生产者发送消息时指定一个时间&#xff0c;消费者不会立刻收到消息&#xff0c;而是在指定时间后才收到消息 用户…...

C++学习:六个月从基础到就业——STL算法(一) 基础与查找算法

C学习&#xff1a;六个月从基础到就业——STL算法&#xff08;一&#xff09; 基础与查找算法 本文是我C学习之旅系列的第二十五篇技术文章&#xff0c;也是第二阶段"C进阶特性"的第三篇&#xff0c;主要介绍C STL算法库的基础知识与查找类算法。查看完整系列目录了解…...

springboot+vue 支付宝支付(沙箱方式,测试环境使用)

准备工作&#xff1a; 1 支付宝沙箱环境的公钥&#xff0c;私钥配置&#xff0c;查询等使用&#xff0c;如果是用自定义的方式&#xff0c;需要下生成公钥&#xff0c;私钥的工具&#xff0c;否则不需要 登录 - 支付宝 小程序文档 - 支付宝文档中心 2 本地测试时&…...

安装win11自带linux是报错:WslRegisterDistribution failed with error: 0x800701bcErr

确保系统设置中的“打开win11的子系统”已打钩 管理员身份运行cmd&#xff0c;并输入如下 然后再重启ubantu...

面试经历(一)雪花算法

uid生成方面 1&#xff1a;为什么用雪花算法 分布式ID的唯一性需要保证&#xff0c;同时需要做到 1&#xff1a;单调递增 2&#xff1a;确保安全&#xff0c;一个是要能体现出递增的单号&#xff0c;二一个不能轻易的被恶意爬出订单数量 3&#xff1a;含有时间戳 4&#…...

docker底层原理简述

前言 平时用docker很多&#xff0c;今天深入了解下docker原理层面的实现&#xff0c;包括docker核心概念&#xff0c;文件系统&#xff0c;资源隔离&#xff0c;网络通信等 参考文章&#xff1a; Docker底层原理&#xff08;图解秒懂史上最全&#xff09; - 疯狂创客圈 - 博…...

【6D位姿估计】Foundation Pose复现

主要参考 项目仓库README站内其他博文 注意事项 容器化部署不难&#xff0c;主要是部署docker本身会存在一些环境问题&#xff0c;重点关注访问外网的端口需要手动调整至与魔法相同&#xff0c;可以参考&#xff1a; docker部署在启动容器镜像后&#xff0c;需要注意镜像当前…...

TDengine 数据订阅设计

简介 数据订阅作为 TDengine 的一个核心功能&#xff0c;为用户提供了灵活获取所需数据的能力。通过深入了解其内部原理&#xff0c;用户可以更加有效地利用这一功能&#xff0c;满足各种实时数据处理和监控需求。 基本概念 主题 与 Kafka 一样&#xff0c;使用 TDengine 数…...

VMware Fusion Pro 13 Mac版虚拟机 安装Win11系统教程

Mac分享吧 文章目录 Win11安装完成&#xff0c;软件打开效果一、VMware安装Windows11虚拟机1️⃣&#xff1a;准备镜像2️⃣&#xff1a;创建虚拟机3️⃣&#xff1a;虚拟机设置4️⃣&#xff1a;安装虚拟机5️⃣&#xff1a;解决连不上网问题 安装完成&#xff01;&#xff0…...

高并发下单库存扣减异常?飞算 JavaAI 自动化生成分布式事务解决方案

在电商、旅游等行业业务量激增&#xff0c;高并发下单场景中&#xff0c;传统库存扣减方式弊端尽显。超卖问题因缺乏有效并发控制机制频发&#xff0c;多个订单同时访问库存数据&#xff0c;导致同一商品多次售出&#xff0c;订单无法履约引发客户投诉&#xff1b;同时&#xf…...

crictl 遇到报错 /run/containerd/containerd.sock: connect: permission denied

报错内容 crictl --runtime-endpoint unix:///run/containerd/containerd.sock logs CONTAINERID FATA[0000] validate service connection: validate CRI v1 runtime API for endpoint "unix:///run/containerd/containerd.sock": rpc error: code Unavailable de…...

CSS外边距合并现象

外边距合并&#xff08;Margin Collapsing&#xff09;是指在文档流中&#xff0c;两个或多个相邻元素的外边距&#xff08;margin&#xff09;会合并为一个外边距&#xff0c;其大小会取其中最大的外边距值 当两个相邻的兄弟元素之间没有其他内容&#xff08;如边框、内边距、…...

《深度神经网络之数据增强、模型保存、模型调用、学习率调整》

文章目录 前言一、数据增强1、什么是数据增强&#xff1f;2、数据增强的实现方法&#xff08;1&#xff09;几何变换翻转:旋转&#xff1a;平移&#xff1a; &#xff08;2&#xff09;颜色变换亮度调整&#xff1a;对比度调整&#xff1a;色彩抖动&#xff1a; &#xff08;3&…...

【Java学习笔记】random的使用

random使用方法 使用说明&#xff1a;返回的是(0<n<1)这个范围中的任意带正号的double值 代码实例 public class helloworld{public static void main(String[] args){System.out.println(Math.random());} }生成0-100中的任意数代码示例 public class Main {public …...

Redis的string类型使用

第一步&#xff1a;添加缓存 以若依岗位代码为例 一&#xff1a;首先从redis中查询岗位信息&#xff0c;如果查询到了则直接返回。 二&#xff1a;如果redis中没有数据&#xff0c;则直接从数据库中查询。查询后放到redis并返回 package com.ruoyi.system.service.impl;imp…...

AIGC架构与原理

AIGC&#xff08;AI Generated Content&#xff0c;人工智能生成内容&#xff09;的架构与原理 AIGC通过整合数据采集、模型训练、推理服务等模块&#xff0c;结合深度学习与生成对抗网络&#xff08;GAN&#xff09;等技术&#xff0c;实现从数据到内容的自动化生成。 一、AIG…...

安全复健|windows常见取证工具

写在前面&#xff1a; 此博客仅用于记录个人学习内容&#xff0c;学识浅薄&#xff0c;若有错误观点欢迎评论区指出。欢迎各位前来交流。&#xff08;部分材料来源网络&#xff0c;若有侵权&#xff0c;立即删除&#xff09; 取证 01系统运行数据 使用工具&#xff1a;Live-F…...

Oracle EBS R12.2 汉化

一、前言 在使用oracle ebs时&#xff0c;使用中文会更好的理解整个ebs流程&#xff0c;以下介绍oracle r12中文补丁的方式 如果你的系统除了支持英语外&#xff0c;还支持其他语言&#xff0c;比如中文&#xff0c;那你在下载补丁的时候除了下载Generic Platform版本外&#…...

【Java面试笔记:基础】12.Java有几种文件拷贝方式?哪一种最高效?

在 Java 中,文件拷贝可以通过多种方式实现,不同方式的性能和适用场景有所差异。 1. Java 文件拷贝方式 传统 IO 方式 使用 FileInputStream 和 FileOutputStream,通过循环读取和写入数据实现文件拷贝。 示例代码: try (InputStream is = new FileInputStream("sou…...

互联网大厂Java面试:RocketMQ、RabbitMQ与Kafka的深度解析

互联网大厂Java面试&#xff1a;RocketMQ、RabbitMQ与Kafka的深度解析 面试场景 面试官&#xff1a;马架构&#xff0c;您好&#xff01;欢迎参加我们的面试。今天我们将围绕消息中间件展开讨论&#xff0c;尤其是RocketMQ、RabbitMQ和Kafka。您有十年的Java研发和架构设计经…...

kali安装切换jdk1.8.0_451java8详细教程

kali安装切换jdk1.8.0_451java8详细教程 下载链接&#xff1a; jdk-8u451-linux-i586.tar.gz 链接: https://pan.baidu.com/s/1lpgI0JMfHpZ__RxsF8UoBw?pwdx3z2 提取码: x3z2 解压jdk 首先将下载好的压缩包放在kali虚拟机中&#xff0c;一般是直接拖到桌面 然后cd到压缩包…...

众趣科技X世界读书日丨数字孪生技术赋能图书馆空间智慧化运营

4月23日&#xff0c;是第30个“世界读书日”&#xff0c;不仅是庆祝阅读的日子&#xff0c;更是思考知识传播未来的契机。 图书馆作为主要传播图书的场所&#xff0c;在科技的发展中&#xff0c;图书馆正面临前所未有的挑战&#xff0c;联合国数据显示&#xff0c;全球近30%的…...

Python内置函数-aiter()

Python内置函数 aiter() 用于获取异步可迭代对象的异步迭代器&#xff0c;是异步编程中的核心工具之一。 1. 基本概念 异步可迭代对象&#xff1a;实现了 __aiter__() 和 __anext__() 方法的对象&#xff0c;支持 async for 循环。 异步迭代器&#xff1a;通过 aiter() 获取的…...

Java 实现单链表翻转(附详细注释)

1. 引言 单链表&#xff08;Singly Linked List&#xff09;是一种常见的数据结构&#xff0c;在算法和数据结构的学习中占有重要地位。翻转单链表是一道经典的面试题&#xff0c;本文将介绍几种常见的 Java 实现方法&#xff0c;并详细讲解关键步骤的含义。 2. 单链表定义 …...

基于HPC的气候模拟GPU加速实践全流程解析

基于HPC的气候模拟GPU加速实践全流程解析 关键词&#xff1a;气候模型、GPU加速、CUDA编程、性能优化、分布式训练 摘要&#xff1a; 本文针对全球气候模拟中10^12级网格点实时计算需求&#xff0c;提出基于CUDA的并行计算架构。通过改进WRF模式的分块矩阵乘法算法&#xff0c…...

【初级】前端开发工程师面试100题(一)

本题库共计包含100题,考察html,css,js,以及react,vue,webpack等基础知识掌握情况。 HTML基础篇 说说你对HTML语义化的理解? 语义化就是用合适的标签表达合适的内容,比如<header>表示页眉,<nav>表示导航。这样不仅代码更清晰,对SEO也友好,屏幕阅读器也能…...

大模型框架技术演进与全栈实践指南

‌一、大模型框架概述 ‌大模型框架‌是支撑大规模语言模型&#xff08;LLM&#xff09;训练、推理和应用开发的核心技术体系&#xff0c;涵盖分布式训练、高效推理、应用编排等全流程。从AlphaGo到GPT-4&#xff0c;大模型框架的进化推动AI从实验室走向工业化落地。据IDC预测…...

【Bug】 [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed

当你在进行深度学习相关操作时&#xff0c;若因缺少本地的 CA 证书而无法下载资源&#xff0c;下面为你介绍几种解决办法&#xff1a; 方法一&#xff1a;更新 CA 证书 在大多数 Linux 发行版中&#xff0c;你可以使用包管理器来更新 CA 证书。例如&#xff0c;在基于 Debian…...

第七章:Workspace Security

Chapter 7: Workspace Security 从变形金刚到安全防护罩&#xff1a;如何为代理设置权限边界&#xff1f; 在上一章多后端配置&#xff0c;我们学会了让代理像变形金刚一样切换不同环境。但就像超级英雄需要遵守法律一样&#xff0c;代理也需要一个“安全防护罩”来限制它的操…...

【论文阅读】Hierarchical Group-Level Emotion Recognition

【论文阅读】Hierarchical Group-Level Emotion Recognition 摘要1.介绍2.相关工作3.方法4.实验5.分析 摘要 本篇博客参考IEEE于2021年收录的论文Hierarchical Group-Level Emotion Recognition&#xff0c;对其主要内容进行总结&#xff0c;以便加深理解和记忆 1.介绍 1&am…...

(2025最新版)CUDA安装及环境配置

CUDA安装 文章目录 CUDA安装检查本地环境下载CUDA安装包CUDA安装检查是否安装成功 学习深度学习的小伙伴在配置环境的时候必不可少的一件事就是安装CUDA&#xff0c;在这个过程中也是容易踩很多坑&#xff0c;所以这里写一篇教程来帮助新入门的小伙伴快速安装CUDA&#xff0c;减…...

ODC 4.3.4 发布:三大核心功能升级,打造更好的数据开发体验

ODC 是OceanBase提供的企业级数据库协同开发平台&#xff0c;提供了团队协作开发的基础框架&#xff0c;和14种工单任务类型。此次升级的 ODC 4.3.4版本&#xff0c;重点优化了30余项功能&#xff0c;主要聚焦快速上手、配置管理和核心功能中的改进&#xff0c;来为用户打造更高…...

JavaFX 第一篇 Hello World

1、简介 JavaFX 是一个用于构建客户端应用程序的 Java 库&#xff0c;作为 Java 标准库的一部分&#xff08;JDK 8 到 10&#xff09;&#xff0c;从 JDK 11 开始&#xff0c;JavaFX 将以独立模块发布&#xff0c;将不再包含在 JDK标准库中&#xff0c;他是 Java 应用程序开发的…...

es的range失效

es的range失效的解决方法 问题描述 当我们es使用keyword类型存储数字时&#xff0c;当我们使用range时我们发现range失效的问题&#xff0c;例如以下的用例&#xff1a; 我们创建一个test1的索引test1&#xff1a; 使用_bulk进行批量导入数据&#xff1a; 进行查询我们发现我…...

gem5-gpu教程03 当前的gem5-gpu软件架构(因为涉及太多专业名词不知道该如何翻译所以没有汉化)

Current gem5-gpu Software Architecture 这是当前gem5-gpu软件架构的示意图。 CudaCore (src/gpu/gpgpu-sim/cuda_core.*, src/gpu/gpgpu-sim/CudaCore.py) Wrapper for GPGPU-Sim shader_core_ctx (gpgpu-sim/gpgpu-sim/shader.h) Sends instruction, global and const m…...

【C++】vector扩容缩容

vector扩容缩容 1 扩容 一般来说&#xff0c;主要是重新分配内存 2 缩容 resize 缩小后&#xff0c;vector 的容量&#xff08;capacity()&#xff09;可能保持不变&#xff0c;需要显式调用 shrink_to_fit() 来释放内存。 验证代码&#xff1a; #include <vector>…...

【鸿蒙HarmonyOS】深入理解router与Navigation

5. 路由 1.页面路由(router模式&#xff09; 1.概述 页面路由指的是在应用程序中实现不同页面之间的跳转&#xff0c;以及数据传递。 我们先明确自定义组件和页面的关系&#xff1a; 自定义组件&#xff1a;Component 装饰的UI单元&#xff0c;页面&#xff1a;即应用的UI…...

手机端touch实现DOM拖拉功能

touch实现DOM拖拉功能 1、面板交互流程图 [ 用户触摸拖动手柄 ]↓ [ 记录起始位置、偏移量 ]↓ [ 实时更新面板 translateY ]↓ [ 手指松开 → 判断释放位置 ]↓ [ 达到恢复条件&#xff1f; → 复位 ]2、详细实现步骤 2.1 初始面板位置 const initialPosition () > tr…...