基于Nodejs作為服務端,React作為前端框架,axios作為通訊框架,實現滑塊驗證

文章目錄

  • 基于Nodejs作為服務端,React作為前端框架,axios作為通訊框架,實現滑塊驗證
  • 1. 為什么要自己寫滑塊驗證
  • 2. 滑塊驗證的整體思路
  • 3. 具體實現
    • 3.1 服務端
    • 3.2 前端
  • 4. 總結

基于Nodejs作為服務端,React作為前端框架,axios作為通訊框架,實現滑塊驗證

1. 為什么要自己寫滑塊驗證

  • 之前我面試一位前端的童鞋,應聘的是高級前端開發工程師,我問他在項目中有沒有實現過滑塊驗證,他說有,我說你怎么做的,他說有很多的現成的框架可以用,我說不用框架,現在想讓你自己設計一個滑塊驗證,你會怎么設計,他支支吾吾好半天,大概表達這么幾點:
    1. 前端實現一個容器圖片和和滑塊圖片
    1. 拖動滑塊圖片,判斷邊界,到達制定邊界則表示驗證成功
  • 這聽起來貌似沒啥問題吧,聽我接下來怎么問,我說你這驗證都放前端了,那不相當于沒驗證,我直接模擬結果不就可以了
  • 他想了想,又說,可以在服務端設定一個坐標點(x,y)然后把前端的坐標點傳過去進行比較,判斷是否完成驗證
  • 也貌似合理,我又問他,那你怎么保證重復驗證和過期驗證,或者說DOS的攻擊
  • 這次他想了很久,最后告訴我說,平時框架用的多,這個真的沒弄過,很誠實,但是能看出來,缺乏思考。
  • 這也是為什么我們要自己寫滑塊驗證的根本原因,保證系統的安全性,防止DOS等安全問題
  • 那具體怎么實現一個滑塊驗證呢,我們來大概闡述一下思路

2. 滑塊驗證的整體思路

    1. 前端領取接口,告知服務端準備驗證
    1. 服務端創建會話,并管理會話周期
    1. 進行DOS攻擊驗證(訪問頻率限制)
    1. 定義主圖尺寸,滑塊尺寸
    1. 生成主圖和滑塊圖
    1. 生成滑塊隨機位置,并保存在會話里
    1. 返回會話ID和圖像
    1. 前端生成生成圖像和滑塊
    1. 監聽開始滑動,滑動,滑動結束等事件
    1. 滑動事件結束后,請求服務端接口,返回會話ID和位置信息
    1. 服務端驗證會話信息和DOS攻擊處理
    1. 服務端驗證是否完整滑塊驗證/成功->返回suc->刪除會話/失敗->返回fail
    1. 前端驗證成功/失敗的邏輯業務

3. 具體實現

3.1 服務端

  • 涉及到的npm包請自行install
// 導入所需的依賴庫
// express:用于創建Web服務器和處理HTTP請求
const express = require('express');
// cors:用于處理跨域資源共享
const cors = require('cors');
// canvas:用于生成驗證圖像
const canvas = require('canvas');
// uuid:用于生成唯一的會話ID
const { v4: uuidv4 } = require('uuid');
// express-rate-limit:用于限制請求頻率,防止惡意攻擊
const rateLimit = require('express-rate-limit');// 創建express應用實例
const app = express();// 啟用CORS中間件,允許跨域請求
app.use(cors());// 啟用JSON解析中間件,用于解析請求體中的JSON數據
app.use(express.json());// 配置請求頻率限制
// 針對生成驗證圖像的接口,限制更嚴格
const generateVerificationLimiter = rateLimit({windowMs: 60 * 1000, // 時間窗口:1分鐘max: 10, // 每個IP在時間窗口內最多允許10次請求message: { success: false, message: '請求過于頻繁,請1分鐘后再試' },standardHeaders: true, // 向客戶端返回速率限制信息legacyHeaders: false, // 禁用舊版速率限制頭keyGenerator: (req) => {// 使用客戶端IP作為限制的鍵return req.ip;}
});// 針對驗證結果的接口,限制相對寬松一些
const verifyLimiter = rateLimit({windowMs: 60 * 1000, // 時間窗口:1分鐘max: 20, // 每個IP在時間窗口內最多允許20次請求message: { success: false, message: '驗證請求過于頻繁,請1分鐘后再試' },standardHeaders: true,legacyHeaders: false,keyGenerator: (req) => {return req.ip;}
});// 創建一個Map對象存儲驗證會話信息
// key: 會話ID,value: 包含缺口位置和時間戳的對象
const verificationSessions = new Map();// 創建一個Map對象存儲IP地址的驗證成功記錄,用于進一步限制
const ipVerificationSuccess = new Map();// 定期清理過期的IP驗證記錄(每小時執行一次)
setInterval(() => {const now = Date.now();const oneHour = 60 * 60 * 1000; // 1小時的毫秒數ipVerificationSuccess.forEach((record, ip) => {// 清理超過1小時的記錄if (now - record.timestamp > oneHour) {ipVerificationSuccess.delete(ip);}});
}, 60 * 60 * 1000);// 定義GET接口,用于生成滑塊驗證圖像
// 應用請求頻率限制中間件
app.get('/api/generate-verification', generateVerificationLimiter, async (req, res) => {try {// 獲取客戶端IPconst clientIp = req.ip;// 檢查該IP最近的驗證成功次數,若過多則進一步限制const successRecord = ipVerificationSuccess.get(clientIp) || { count: 0, timestamp: Date.now() };if (successRecord.count > 50) { // 1小時內超過50次驗證成功,可能是自動化程序return res.status(429).json({success: false,message: '您的操作過于頻繁,請稍后再試'});}// 定義驗證圖像的尺寸參數// 主圖像寬度const width = 300;// 主圖像高度const height = 150;// 滑塊(拼圖)的尺寸const puzzleSize = 40;// 隨機生成缺口位置(確保在圖像范圍內)// 水平位置:確保滑塊不會超出圖像左側和右側邊界const puzzleX = Math.floor(Math.random() * (width - puzzleSize * 2)) + puzzleSize;// 垂直位置:確保滑塊不會超出圖像頂部和底部邊界const puzzleY = Math.floor(Math.random() * (height - puzzleSize));// 創建主畫布(帶缺口的背景圖)const mainCanvas = canvas.createCanvas(width, height);// 獲取2D繪圖上下文,用于繪制圖像const mainCtx = mainCanvas.getContext('2d');// 繪制背景色(淺灰色)mainCtx.fillStyle = '#f0f0f0';// 填充整個畫布mainCtx.fillRect(0, 0, width, height);// 繪制一些隨機形狀作為背景干擾元素,增加驗證難度// 循環繪制10個隨機圓形for (let i = 0; i < 10; i++) {// 隨機生成一個柔和的顏色(RGB值在100-200之間,透明度0.5)mainCtx.fillStyle = `rgba(${Math.random() * 100 + 100}, ${Math.random() * 100 + 100}, ${Math.random() * 100 + 100}, 0.5)`;// 隨機生成圓的大小(5-25像素)const size = Math.random() * 20 + 5;// 開始繪制路徑mainCtx.beginPath();// 繪制圓形mainCtx.arc(// 隨機X坐標Math.random() * width,// 隨機Y坐標Math.random() * height,// 半徑size,// 起始角度(0弧度)0,// 結束角度(2π弧度,即360度)Math.PI * 2);// 填充圓形mainCtx.fill();}// 創建滑塊(拼圖)畫布,用于繪制需要用戶拖動的部分const puzzleCanvas = canvas.createCanvas(puzzleSize, puzzleSize);// 獲取滑塊畫布的2D繪圖上下文const puzzleCtx = puzzleCanvas.getContext('2d');// 從主畫布復制缺口區域到滑塊畫布// 這樣滑塊就包含了缺口處的圖像內容puzzleCtx.drawImage(mainCanvas,          // 源圖像(主畫布)puzzleX, puzzleY,    // 源圖像中要復制的區域的左上角坐標puzzleSize, puzzleSize,  // 源圖像中要復制的區域的寬度和高度0, 0,                // 目標畫布(滑塊畫布)中放置圖像的左上角坐標puzzleSize, puzzleSize   // 目標畫布中圖像的寬度和高度);// 在主畫布上繪制缺口(挖空效果)// 使用背景色填充缺口區域,造成"缺失"的效果mainCtx.fillStyle = '#f0f0f0';mainCtx.fillRect(puzzleX, puzzleY, puzzleSize, puzzleSize);// 為缺口添加邊框,使其更明顯mainCtx.strokeStyle = '#ccc';  // 邊框顏色(淺灰色)mainCtx.lineWidth = 2;         // 邊框寬度mainCtx.strokeRect(puzzleX, puzzleY, puzzleSize, puzzleSize);  // 繪制矩形邊框// 生成唯一的會話ID,用于標識本次驗證const sessionId = uuidv4();// 存儲會話信息(缺口位置和生成時間)verificationSessions.set(sessionId, {puzzleX,          // 缺口的X坐標puzzleY,          // 缺口的Y坐標timestamp: Date.now(),  // 會話生成時間戳clientIp          // 記錄請求的IP地址,用于額外安全檢查});// 設置定時清理過期會話(5分鐘后)// 防止內存泄漏和重復使用舊會話setTimeout(() => {verificationSessions.delete(sessionId);}, 5 * 60 * 1000);  // 5分鐘 = 5 * 60 * 1000毫秒// 將生成的圖像和會話信息返回給客戶端res.json({sessionId,        // 會話IDmainImage: mainCanvas.toDataURL('image/png'),  // 主圖像(帶缺口)的DataURLpuzzleImage: puzzleCanvas.toDataURL('image/png'),  // 滑塊圖像的DataURLpuzzleSize        // 滑塊尺寸});} catch (error) {// 捕獲并處理異常console.error('生成驗證圖像失敗:', error);// 向客戶端返回錯誤信息res.status(500).json({ error: '生成驗證圖像失敗' });}
});// 定義POST接口,用于驗證用戶滑動的結果
// 應用請求頻率限制中間件
app.post('/api/verify', verifyLimiter, (req, res) => {// 從請求體中獲取會話ID和用戶滑動的最終X坐標const { sessionId, positionX } = req.body;// 獲取客戶端IPconst clientIp = req.ip;// 檢查會話是否存在const session = verificationSessions.get(sessionId);if (!session) {// 如果會話不存在或已過期,返回驗證失敗return res.json({ success: false, message: '驗證會話已過期,請重試' });}// 檢查會話的IP是否與當前請求IP一致,防止會話劫持if (session.clientIp !== clientIp) {verificationSessions.delete(sessionId); // 刪除可疑會話return res.json({ success: false, message: '驗證異常,請重試' });}// 驗證完成后移除會話(防止重復使用同一個會話進行驗證)verificationSessions.delete(sessionId);// 檢查用戶滑動的位置是否在可接受范圍內// 允許±5像素的誤差,提高用戶體驗const tolerance = 5;const isSuccess = Math.abs(positionX - session.puzzleX) <= tolerance;// 如果驗證成功,更新該IP的成功記錄if (isSuccess) {const now = Date.now();const successRecord = ipVerificationSuccess.get(clientIp) || { count: 0, timestamp: now };// 更新記錄:計數+1,更新時間戳ipVerificationSuccess.set(clientIp, {count: successRecord.count + 1,timestamp: now});}// 向客戶端返回驗證結果res.json({success: isSuccess,message: isSuccess ? '驗證成功' : '驗證失敗,請重試'});
});// 定義服務器監聽的端口號
const PORT = process.env.PORT || 5000;// 啟動服務器
app.listen(PORT, () => {console.log(`服務器運行在端口 ${PORT}`);console.log(`已啟用請求頻率限制保護`);
});

3.2 前端

  • 使用React hooks實現
  • 滑塊組件SliderVerification.jsx
// 導入React庫和所需的鉤子函數
import React, { useState, useEffect, useRef } from 'react';
// 導入axios用于發送HTTP請求
import axios from 'axios';// 定義滑塊驗證組件
// onVerifySuccess: 驗證成功時的回調函數
const SliderVerification = ({ onVerifySuccess }) => {// 狀態管理 - 圖像相關// 主圖像(帶缺口)的DataURLconst [mainImage, setMainImage] = useState('');// 滑塊(拼圖)圖像的DataURLconst [puzzleImage, setPuzzleImage] = useState('');// 滑塊的尺寸const [puzzleSize, setPuzzleSize] = useState(40);// 本次驗證的會話IDconst [sessionId, setSessionId] = useState('');// 狀態管理 - 交互相關// 是否正在拖動滑塊const [isDragging, setIsDragging] = useState(false);// 滑塊當前的X坐標位置const [positionX, setPositionX] = useState(0);// 顯示給用戶的提示信息const [message, setMessage] = useState('請拖動滑塊完成驗證');// 是否正在驗證過程中(等待后端響應)const [isVerifying, setIsVerifying] = useState(false);// 驗證結果:null(未驗證)、true(成功)、false(失敗)const [isSuccess, setIsSuccess] = useState(null);// 創建引用,用于訪問DOM元素// 滑塊元素的引用const sliderRef = useRef(null);// 拼圖元素的引用const puzzleRef = useRef(null);// 整個驗證容器的引用const containerRef = useRef(null);// 組件掛載時初始化驗證useEffect(() => {// 從后端獲取驗證圖像fetchVerificationImage();}, []);  // 空依賴數組表示只在組件掛載時執行一次// 從后端獲取驗證圖像的函數const fetchVerificationImage = async () => {try {// 更新提示信息setMessage('加載驗證圖像中...');// 向后端發送請求,獲取驗證圖像const response = await axios.get('http://localhost:5000/api/generate-verification');// 從響應中提取數據const { sessionId, mainImage, puzzleImage, puzzleSize } = response.data;// 更新狀態setSessionId(sessionId);         // 保存會話IDsetMainImage(mainImage);         // 保存主圖像setPuzzleImage(puzzleImage);     // 保存滑塊圖像setPuzzleSize(puzzleSize);       // 保存滑塊尺寸setPositionX(0);                 // 重置滑塊位置到初始位置setMessage('請拖動滑塊完成驗證'); // 重置提示信息setIsSuccess(null);              // 重置驗證結果} catch (error) {// 處理請求失敗的情況console.error('獲取驗證圖像失敗:', error);setMessage('加載驗證失敗,請刷新重試');}};// 處理鼠標/觸摸開始事件(用戶開始拖動滑塊)const handleStart = (e) => {// 如果正在驗證中或已經驗證過,則不執行任何操作if (isVerifying || isSuccess !== null) return;// 設置正在拖動狀態為truesetIsDragging(true);// 防止拖動時選中文本或其他默認行為e.preventDefault();};// 處理鼠標/觸摸移動事件(用戶拖動滑塊過程中)const handleMove = (e) => {// 如果不在拖動狀態,則不執行任何操作if (!isDragging) return;// 獲取容器元素的位置信息(相對于視口)const containerRect = containerRef.current.getBoundingClientRect();// 存儲鼠標或觸摸點的X坐標let clientX;// 區分鼠標事件和觸摸事件,獲取對應的X坐標if (e.type.includes('mouse')) {// 鼠標事件clientX = e.clientX;} else {// 觸摸事件(取第一個觸摸點)clientX = e.touches[0].clientX;}// 計算滑塊相對于容器的位置// 減去容器左邊界和滑塊一半寬度,使滑塊中心與鼠標/觸摸點對齊let newPositionX = clientX - containerRect.left - puzzleSize / 2;// 限制滑塊在容器范圍內移動// 最大X坐標 = 容器寬度 - 滑塊寬度const maxX = containerRect.width - puzzleSize;// 確保滑塊不會超出左邊界(最小0)和右邊界(最大maxX)newPositionX = Math.max(0, Math.min(newPositionX, maxX));// 更新滑塊位置狀態setPositionX(newPositionX);};// 處理鼠標/觸摸結束事件(用戶釋放滑塊)const handleEnd = async () => {// 如果不在拖動狀態,則不執行任何操作if (!isDragging) return;// 結束拖動狀態setIsDragging(false);// 設置正在驗證狀態setIsVerifying(true);// 更新提示信息setMessage('驗證中...');try {// 向后端發送驗證請求,包含會話ID和滑塊最終位置const response = await axios.post('http://localhost:5000/api/verify', {sessionId,    // 會話ID,用于標識本次驗證positionX     // 滑塊最終的X坐標位置});// 從響應中提取驗證結果const { success, message } = response.data;// 更新狀態setIsSuccess(success);       // 保存驗證結果setMessage(message);         // 更新提示信息setIsVerifying(false);       // 結束驗證狀態// 如果驗證成功且提供了成功回調函數,則調用回調if (success && onVerifySuccess) {onVerifySuccess();}} catch (error) {// 處理驗證請求失敗的情況console.error('驗證失敗:', error);setMessage('驗證失敗,請重試');setIsVerifying(false);}};// 重新加載驗證圖像(用戶點擊刷新按鈕時調用)const handleRefresh = () => {fetchVerificationImage();};// 渲染組件UIreturn (<div className="verification-container" ref={containerRef} style={{width: '300px',border: '1px solid #ddd',borderRadius: '8px',padding: '15px',boxShadow: '0 2px 10px rgba(0,0,0,0.1)',background: '#fff'}}>{/* 驗證圖像區域 */}<div className="image-container" style={{width: '100%',height: '150px',position: 'relative',  // 相對定位,使內部元素可以絕對定位overflow: 'hidden',    // 隱藏超出容器的部分borderRadius: '4px',marginBottom: '15px'}}>{/* 主圖像(帶缺口) */}{mainImage && (<img src={mainImage} alt="驗證背景圖,包含一個需要填充的缺口" style={{ width: '100%', height: '100%', objectFit: 'cover' }}/>)}{/* 滑塊拼圖 */}{puzzleImage && (<div ref={puzzleRef}style={{position: 'absolute',  // 絕對定位,可通過left屬性控制位置left: `${positionX}px`, // 動態設置left值,控制滑塊水平位置top: '0',width: `${puzzleSize}px`,height: `${puzzleSize}px`,boxShadow: '0 0 10px rgba(0,0,0,0.3)', // 添加陰影,增強立體感pointerEvents: 'none'  // 使鼠標事件穿透此元素,不干擾拖動}}><img src={puzzleImage} alt="需要拖動到缺口位置的滑塊拼圖" style={{ width: '100%', height: '100%', objectFit: 'cover' }}/></div>)}</div>{/* 滑塊區域 */}<div className="slider-container" style={{width: '100%',height: '40px',background: '#f0f0f0',borderRadius: '20px',position: 'relative',  // 相對定位,使內部元素可以絕對定位overflow: 'hidden'     // 隱藏超出容器的部分}}>{/* 滑塊軌道進度條 */}<div style={{// 根據滑塊位置設置進度條寬度width: isDragging || isSuccess ? `${(positionX / (300 - puzzleSize)) * 100}%` : '0%',height: '100%',// 驗證成功時顯示綠色,否則顯示藍色background: isSuccess ? '#52c41a' : '#1890ff',// 驗證成功時添加過渡動畫transition: isSuccess ? 'width 0.3s' : 'none'}}/>{/* 滑塊按鈕 */}<divref={sliderRef}style={{position: 'absolute',  // 絕對定位,可通過left屬性控制位置left: `${positionX}px`, // 動態設置left值,控制滑塊水平位置top: '0',width: '40px',height: '40px',background: '#fff',border: '1px solid #ddd',borderRadius: '50%',   // 圓形滑塊display: 'flex',alignItems: 'center',justifyContent: 'center',cursor: 'pointer',boxShadow: '0 2px 5px rgba(0,0,0,0.2)' // 添加陰影,增強立體感}}// 鼠標事件處理onMouseDown={handleStart}  // 鼠標按下時開始拖動onMouseMove={handleMove}   // 鼠標移動時更新位置onMouseUp={handleEnd}      // 鼠標釋放時結束拖動并驗證onMouseLeave={handleEnd}   // 鼠標離開滑塊區域時結束拖動// 觸摸事件處理(適配移動設備)onTouchStart={handleStart} // 觸摸開始時開始拖動onTouchMove={handleMove}   // 觸摸移動時更新位置onTouchEnd={handleEnd}     // 觸摸結束時結束拖動并驗證>{/* 滑塊圖標 */}<svg width="20" height="20" viewBox="0 0 24 24" fill="none" // 驗證成功時顯示綠色,否則顯示藍色stroke={isSuccess ? '#52c41a' : '#1890ff'} strokeWidth="2"style={{ pointerEvents: 'none' }} // 使鼠標事件穿透圖標>{/* 根據驗證結果顯示不同圖標 */}{isSuccess ? (// 驗證成功時顯示對勾圖標<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" />) : (// 未驗證或驗證失敗時顯示箭頭圖標<path d="M5 12h14M12 5l7 7-7 7" />)}</svg></div>{/* 滑塊區域文字提示 */}<div style={{position: 'absolute',width: '100%',height: '100%',display: 'flex',alignItems: 'center',justifyContent: 'center',pointerEvents: 'none',  // 使鼠標事件穿透文字fontSize: '14px',// 驗證成功時顯示綠色,否則顯示灰色color: isSuccess ? '#52c41a' : '#666'}}>{message}</div></div>{/* 刷新按鈕,用于重新加載驗證圖像 */}<button onClick={handleRefresh}style={{marginTop: '10px',background: 'none',border: 'none',color: '#1890ff',cursor: 'pointer',fontSize: '12px',display: 'flex',alignItems: 'center',padding: '5px 0',}}>{/* 刷新圖標 */}<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#1890ff" strokeWidth="2" style={{ marginRight: '5px' }}><path d="M23 4v6h-6M1 20v-6h6" /><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15" /></svg>刷新驗證</button></div>);
};// 導出組件,供其他組件使用
export default SliderVerification;
  • 主組件App.jsx
// 導入React庫和useState鉤子
import React, { useState } from 'react';
// 導入滑塊驗證組件
import SliderVerification from './SliderVerification';// 定義應用的主組件
const App = () => {// 狀態管理:記錄用戶是否已通過驗證// 初始值為false,表示未驗證const [isVerified, setIsVerified] = useState(false);// 處理驗證成功的函數const handleVerificationSuccess = () => {// 設置驗證狀態為已驗證setIsVerified(true);// 這里可以添加驗證成功后的邏輯,如跳轉到下一頁、提交表單等// 例如:可以調用API獲取用戶數據,或者顯示受保護的內容};// 渲染應用UIreturn (<div style={{// 使用flex布局使內容居中顯示display: 'flex',flexDirection: 'column',alignItems: 'center',justifyContent: 'center',minHeight: '100vh',  // 最小高度為視口高度,確保內容垂直居中background: '#f5f5f5', // 淺灰色背景padding: '20px'       // 內邊距,防止內容貼邊}}>{/* 頁面標題 */}<h2 style={{ color: '#333', marginBottom: '30px' }}>滑塊驗證示例</h2>{/* 根據驗證狀態顯示不同內容 */}{!isVerified ? (// 未驗證時顯示驗證提示和滑塊驗證組件<div><p style={{ color: '#666', textAlign: 'center', marginBottom: '20px' }}>請完成下方滑塊驗證以證明您不是機器人</p>{/* 滑塊驗證組件 */}{/* 傳入驗證成功的回調函數 */}<SliderVerification onVerifySuccess={handleVerificationSuccess} /></div>) : (// 已驗證時顯示成功信息<div style={{textAlign: 'center',padding: '30px',background: 'white',borderRadius: '8px',boxShadow: '0 2px 10px rgba(0,0,0,0.1)' // 添加陰影,增強立體感}}>{/* 成功圖標 */}<svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="#52c41a" strokeWidth="2" style={{ margin: '0 auto 20px' }}><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" /><polyline points="22 4 12 14.01 9 11.01" /></svg>{/* 成功標題 */}<h3 style={{ color: '#333', marginBottom: '10px' }}>驗證成功!</h3>{/* 成功信息 */}<p style={{ color: '#666' }}>您已成功完成驗證,可以繼續使用服務。</p></div>)}</div>);
};// 導出主組件
export default App;

4. 總結

  • 作為一個高級前端開發工程師或者再往上技術專家/架構師,一定要有自己設計實現的思考能力,才能在具體的業務中做到安全防控

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/94072.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/94072.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/94072.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

2025年物流大數據分析的主要趨勢

大數據已為物流行業帶來革命性變革&#xff0c;助力實現更智能的運營與實時洞察。如今&#xff0c;企業可精準識別瓶頸、優化供應鏈&#xff1b;自疫情以來&#xff0c;大數據的采用率大幅攀升&#xff0c;79% 的供應鏈負責人將分析培訓列為優先事項。這一轉變不僅提升了效率、…

【C2000常見問題】JTAG仿真器類型和JTAG Debug定位方法

【C2000常見問題】JTAG仿真器類型和JTAG Debug定位方法 母線繼電保護動作行為仿真分析系統 【C2000常見問題】JTAG仿真器類型和JTAG Debug定位方法 1問題背景 2問題分析 3可能出現的問題 4JTAG問題總結 1問題背景 某客戶產品應用中,使用JTAG仿真器時經常會遇到一啟動負載或者…

LT8712SX,Type-C/DP1.4 /eDP轉 DP1.4/HD-DVI2.0 帶音頻

簡介LT8712SX是一款高性能Type-C/DP1.4 /eDP轉 DP1.4/HD-DVI2.0 帶音頻,支持4K(3840*2316)60Hz 的分辨率,提供 I2S 和 SPDIF 兩個數字音頻輸出接口&#xff0c;均支持 8 通道 LPCM 或壓縮音頻&#xff0c;最高采樣率為 192KHz。應用場景便攜式顯示器例如&#xff0c;手機通過 T…

C語言基礎:(二十)自定義類型:結構體

目錄 前言 一、結構體類型的聲明 1.1 結構體回顧 1.1.1 結構體的聲明 1.1.2 結構體變量的創建和初始化 1.2 結構的特殊聲明 1.3 結構的自引用 二、結構體內存對齊 2.1 對齊規則 2.1.1 練習1 2.1.2 練習2 2.1.3 練習3&#xff1a;結構體嵌套問題 2.2 為什…

數據倉庫分層解析(詳細)

目錄 一、數據倉庫為什么要分層 二、數據倉庫怎么分層 1、ODS&#xff08;Operational Data Store&#xff09;&#xff1a;數據源層 2、DW&#xff08;Data Warehouse&#xff09;&#xff1a; 數據倉庫層 2.1、DWD&#xff08;Data Warehouse Detail&#xff09;&#x…

智慧城管云平臺源碼,微服務vue+element+springboot+uniapp技術架構,數字化綜合執法辦案系統

智慧城管綜合執法系統源碼&#xff0c;包括PC端和移動端。微服務架構&#xff0c;vueelementspringbootuniapp技術框架開發。智慧城管建立了統一的城管執法案件數據庫、法律法規庫、檔案信息庫等&#xff0c;支持簡易程序案件、一般程序案件、行政強制管理等執法業務的辦理&…

VUE實現多個彈窗優先級變化實現思路

在開發復雜的單頁應用&#xff08;SPA&#xff09;時&#xff0c;我們經常會遇到需要管理多個浮動窗口&#xff08;或稱“彈窗”、“面板”&#xff09;的場景。一個核心的用戶體驗要求是&#xff1a;用戶當前操作的窗口應該總是在最頂層。本文將結合代碼示例&#xff0c;總結一…

集成算法和kmeans

一、集成算法&#xff08;Ensemble Learning&#xff09; 1. 基本概念 集成學習通過構建并結合多個學習器&#xff08;基分類器/回歸器&#xff09;來完成學習任務&#xff0c;旨在通過集體決策提升模型性能&#xff0c;類似于“多個專家的綜合判斷優于單個專家”。 2. 結合策略…

圖數據庫性能與可擴展性評估

圖數據庫的性能與可擴展性直接決定業務場景&#xff08;如實時風控、知識圖譜分析&#xff09;的落地效果&#xff0c;需結合業務場景特性&#xff08;OLTP/OLAP&#xff09;、技術指標&#xff08;響應時間、吞吐量&#xff09;和擴展能力&#xff08;數據量/節點擴展&#xf…

樹莓派常用的國內鏡像源列表以及配置方法

1. 常用的鏡像源使用下來發現清華源經常訪問不到&#xff0c;阿里源比較好用。其他源還未測試。源名稱URL清華源https://pypi.tuna.tsinghua.edu.cn/simple阿里云https://mirrors.aliyun.com/pypi/simple/中科大https://pypi.mirrors.ustc.edu.cn/simple/華為云https://repo.hu…

Transformer在文本、圖像和點云數據中的應用——經典工作梳理

摘要 最近在整一些3D檢測和分割的任務&#xff0c;接觸了一下ptv3&#xff0c;在之前梳理的工作owlv2中用到了vit&#xff0c;去年年假閱讀《多模態大模型&#xff1a;算法、應用與微調》&#xff08;劉兆峰&#xff09;時學習了Transformer網絡架構及其在文本數據中的應用&am…

訓練后數據集后部署PaddleOCR轉trt流程

訓練后的模型部署&#xff0c;首先要進行訓練 0.訓練流程見文章 PaddleOCR字符識別&#xff0c;訓練自己的數據集全流程&#xff08;環境、標注、訓練、推理&#xff09;-CSDN博客文章瀏覽閱讀1.6k次&#xff0c;點贊53次&#xff0c;收藏23次。PaddleOCR是基于百度飛槳框架的…

《MLB美職棒》美國國球是橄欖球還是棒球·棒球5號位

USAs National Sport Showdown: MLB?? vs NFL Ultimate Guide!從商業價值到文化基因&#xff0c;360解析美國體育王座之爭&#xff01;添加圖片注釋&#xff0c;不超過 140 字&#xff08;可選&#xff09;? 歷史定位 Historical Roots?? MLB&#xff1a;The "Classi…

常見 Linux 網絡命令梳理

在日常運維和排障工作中&#xff0c;網絡相關命令是最常用的一類工具。無論是檢查網絡連通性&#xff0c;還是定位路由問題&#xff0c;又或是分析端口和服務占用&#xff0c;熟悉這些命令都能讓我們更高效地解決問題。本文將從幾個常見的維度來梳理 Linux 下的網絡命令&#x…

Docker 搭建 Gitlab 實現自動部署Vue項目

1、配置要求: 硬件要求: CPU:雙核或以上 內存:4GB或以上 軟件要求:Centos6 或更高版本 2、gitlab鏡像: # 中文版倉庫 #docker pull twang2218/gitlab-ce-zh docker pull gitlab/gitlab-ce 3、gitlab部署目錄 說明:為了跟其他容器區分,gitlab相關容…

如何解決機器翻譯的“幻覺“問題(Hallucination)?

更多內容請見: 機器翻譯修煉-專欄介紹和目錄 文章目錄 一、數據層面優化 二、模型架構改進 三、訓練策略調整 四、評估與迭代 五、前沿方向與挑戰 六、案例:WMT2023幻覺緩解方案 機器翻譯中的“幻覺”(Hallucination)指模型生成與源文本語義無關、邏輯矛盾或事實錯誤的翻譯…

基于STM32+NBIOT設計的宿舍安防控制系統_264

文章目錄 1.1 項目介紹 【1】開發背景 【2】實現需求 【3】項目硬件模塊組成 【4】設計意義 【5】國內外研究現狀 【6】摘要 1.2 系統總體設計 【1】系統功能需求分析 【2】系統總體方案設計 【3】系統工作原理 1.3 系統框架圖 1.4 系統功能總結 1.5 系統原理圖 1.6 實物圖 1.7…

SLAM文獻之-Globally Consistent and Tightly Coupled 3D LiDAR Inertial Mapping

一、簡介 該論《Globally Consistent and Tightly Coupled 3D LiDAR Inertial Mapping》是日本先進工業科學技術研究所&#xff08;AIST&#xff09;的Koide等人于2022年在IEEE國際機器人與自動化會議&#xff08;ICRA&#xff09;上發表的一篇論文。該研究提出了一種基于全局…

【STM32】HAL庫中的實現(七):DMA(直接存儲器訪問)

DMA 是什么&#xff1f; DMA&#xff08;Direct Memory Access&#xff09;是 外設直接和內存之間數據搬運的機制&#xff0c;不需要 CPU 參與。 ? 舉個例子&#xff1a;傳統方式&#xff1a; ADC → CPU → RAM 使用 DMA&#xff1a;ADC → DMA → RAM&#xff08;CPU 不需干…

【LeetCode熱題100道筆記+動畫】字母異位詞分組

題目描述 給你一個字符串數組,請你將 字母異位詞 組合在一起。可以按任意順序返回結果列表。 示例 1: 輸入: strs = [“eat”, “tea”, “tan”, “ate”, “nat”, “bat”] 輸出: [[“bat”],[“nat”,“tan”],[“ate”,“eat”,“tea”]] 解釋: 在 strs 中沒有字符串可…