基于Vue3+WebSocket實現(xiàn)實時文件傳輸監(jiān)控系統(tǒng)
更新時間:2025年03月26日 10:20:45 作者:北辰alk
WebSocket是一種在客戶端和服務器之間進行雙向通信的網(wǎng)絡協(xié)議,它通過建立持久性的、全雙工的連接,允許服務器主動向客戶端發(fā)送數(shù)據(jù),本文小編給大家介紹了基于Vue3+WebSocket實現(xiàn)實時文件傳輸監(jiān)控系統(tǒng),需要的朋友可以參考下
一、需求分析與技術(shù)選型
1.1 實時監(jiān)控系統(tǒng)核心需求
- 實時顯示文件傳輸進度
- 動態(tài)更新傳輸速度/剩余時間
- 多任務并行狀態(tài)管理
- 異常中斷實時告警
- 歷史傳輸記錄可視化
1.2 技術(shù)架構(gòu)設計
1.3 技術(shù)棧說明
- 前端:Vue3 + Element Plus + ECharts
- 實時通信:WebSocket + Socket.IO
- 后端:Node.js + Express + WebSocket
- 數(shù)據(jù)存儲:MongoDB(歷史記錄)
- 輔助庫:dayjs(時間處理)、lodash(數(shù)據(jù)處理)
二、環(huán)境搭建與配置
2.1 創(chuàng)建Vue3項目
npm create vue@latest # 選擇TypeScript、Router、Pinia
2.2 安裝核心依賴
npm install socket.io-client @types/socket.io-client echarts element-plus
2.3 WebSocket服務端搭建
// server/ws-server.js const express = require('express') const { createServer } = require('http') const { Server } = require('socket.io') const app = express() const httpServer = createServer(app) const io = new Server(httpServer, { cors: { origin: "http://localhost:5173", methods: ["GET", "POST"] } }) // 連接管理 const activeConnections = new Map() io.on('connection', (socket) => { console.log(`客戶端連接: ${socket.id}`) // 身份驗證 socket.on('auth', (token) => { const userId = verifyToken(token) // 鑒權(quán)邏輯 activeConnections.set(userId, socket) }) // 斷開處理 socket.on('disconnect', () => { activeConnections.delete(socket.userId) }) }) httpServer.listen(3001)
三、前端WebSocket集成
3.1 Socket服務封裝
// src/utils/socket.ts import { io, Socket } from 'socket.io-client' class SocketService { private static instance: SocketService private socket: Socket | null = null constructor() { this.initSocket() } public static getInstance(): SocketService { if (!SocketService.instance) { SocketService.instance = new SocketService() } return SocketService.instance } private initSocket(): void { this.socket = io('http://localhost:3001', { transports: ['websocket'], auth: { token: localStorage.getItem('token') } }) this.socket.on('connect', () => { console.log('WebSocket連接成功') }) this.socket.on('exception', (error) => { console.error('WebSocket錯誤:', error) }) } // 監(jiān)聽傳輸事件 public onFileProgress(callback: (data: TransferData) => void): void { this.socket?.on('file-progress', callback) } // 發(fā)送控制命令 public sendControl(command: ControlCommand): void { this.socket?.emit('transfer-control', command) } } export const socket = SocketService.getInstance()
四、實時監(jiān)控界面開發(fā)
4.1 傳輸任務列表組件
<template> <el-table :data="tasks" style="width: 100%"> <el-table-column prop="fileName" label="文件名" /> <el-table-column label="進度"> <template #default="{ row }"> <el-progress :percentage="row.progress" :status="getStatus(row)" /> </template> </el-table-column> <el-table-column label="速度"> <template #default="{ row }"> {{ formatSpeed(row.speed) }} </template> </el-table-column> <el-table-column label="操作"> <template #default="{ row }"> <el-button @click="handlePause(row.id)">暫停</el-button> </template> </el-table-column> </el-table> </template>
4.2 實時速度圖表(ECharts集成)
<script setup lang="ts"> import * as echarts from 'echarts' const chartRef = ref<HTMLElement>() let chart: echarts.ECharts onMounted(() => { chart = echarts.init(chartRef.value) const option = { xAxis: { type: 'time' }, yAxis: { name: '速度 (MB/s)' }, series: [{ data: [], type: 'line', smooth: true }] } chart.setOption(option) }) // 更新圖表數(shù)據(jù) const updateChart = (newData: SpeedPoint[]) => { chart.setOption({ series: [{ data: newData }] }) } </script>
五、文件傳輸核心邏輯
5.1 增強版文件上傳器
class EnhancedFileUploader { private ws: WebSocket private file: File private chunkSize: number private uploadedBytes = 0 constructor(file: File) { this.file = file this.chunkSize = this.calculateChunkSize() this.ws = new WebSocket('ws://localhost:3001/upload') } private calculateChunkSize(): number { // 根據(jù)網(wǎng)絡速度動態(tài)調(diào)整分片大小 const baseSize = 1024 * 1024 // 1MB return navigator.connection?.downlink ? baseSize * Math.floor(navigator.connection.downlink / 5) : baseSize } async startUpload() { const reader = new FileReader() let offset = 0 const readChunk = () => { const chunk = this.file.slice(offset, offset + this.chunkSize) reader.readAsArrayBuffer(chunk) } reader.onload = (e) => { if (e.target?.result) { this.ws.send(e.target.result) this.uploadedBytes += this.chunkSize this.reportProgress() if (offset < this.file.size) { offset += this.chunkSize readChunk() } } } readChunk() } private reportProgress() { const progress = (this.uploadedBytes / this.file.size) * 100 const speed = this.calculateSpeed() socket.sendControl({ type: 'progress', data: { fileId: this.file.name, progress: Number(progress.toFixed(1)), speed: speed, remaining: this.calculateRemainingTime(speed) } }) } }
5.2 速度計算算法
class SpeedCalculator { private records: Array<{ time: number; bytes: number }> = [] update(bytes: number): void { this.records.push({ time: Date.now(), bytes: bytes }) // 保留最近10秒記錄 if (this.records.length > 10) { this.records.shift() } } get currentSpeed(): number { if (this.records.length < 2) return 0 const first = this.records[0] const last = this.records[this.records.length - 1] const timeDiff = (last.time - first.time) / 1000 const bytesDiff = last.bytes - first.bytes return bytesDiff / timeDiff // bytes/sec } }
六、服務端實時處理
6.1 WebSocket事件處理器
// 文件傳輸事件處理 io.on('connection', (socket) => { socket.on('file-upload', async (chunk, ack) => { try { const filePath = path.join(uploadDir, fileName) await fs.promises.appendFile(filePath, Buffer.from(chunk)) ack({ status: 'success', received: chunk.length }) // 廣播進度更新 io.emit('file-progress', { fileId: fileName, progress: calculateProgress(), speed: currentSpeed }) } catch (error) { ack({ status: 'error', message: error.message }) } }) // 傳輸控制命令 socket.on('transfer-control', (command) => { switch (command.type) { case 'pause': handlePause(command.fileId) break case 'resume': handleResume(command.fileId) break case 'cancel': handleCancel(command.fileId) break } }) })
6.2 傳輸狀態(tài)管理
class TransferManager { constructor() { this.transfers = new Map() } addTransfer(fileId, fileSize) { this.transfers.set(fileId, { startTime: Date.now(), bytesTransferred: 0, status: 'uploading' }) } updateProgress(fileId, bytes) { const transfer = this.transfers.get(fileId) transfer.bytesTransferred += bytes transfer.lastUpdate = Date.now() } getTransferStatus(fileId) { return this.transfers.get(fileId) || null } }
七、高級功能實現(xiàn)
7.1 斷線自動重連
// 前端重連邏輯 class ReconnectManager { private retries = 0 private maxRetries = 5 constructor(private socket: Socket) { socket.on('disconnect', () => { this.scheduleReconnect() }) } private scheduleReconnect() { if (this.retries < this.maxRetries) { setTimeout(() => { this.socket.connect() this.retries++ }, Math.min(1000 * 2 ** this.retries, 30000)) } } }
7.2 帶寬節(jié)流控制
class BandwidthThrottle { private tokens: number private lastFill: number constructor( private capacity: number, // 帶寬容量(bytes/sec) private interval = 1000 // 令牌填充間隔 ) { this.tokens = capacity this.lastFill = Date.now() } async consume(bytes: number): Promise<void> { const now = Date.now() const elapsed = now - this.lastFill if (elapsed > this.interval) { this.tokens = this.capacity this.lastFill = now } if (this.tokens >= bytes) { this.tokens -= bytes return } const waitTime = (bytes - this.tokens) / this.capacity * 1000 await new Promise(resolve => setTimeout(resolve, waitTime)) this.tokens = this.capacity - (bytes - this.tokens) this.lastFill = Date.now() } }
7.3 傳輸質(zhì)量監(jiān)控
class NetworkMonitor { private latencyHistory: number[] = [] private packetLossCount = 0 start() { setInterval(async () => { const latency = await this.measureLatency() this.latencyHistory.push(latency) if (latency > 1000) { this.emit('high-latency', latency) } }, 5000) } private measureLatency(): Promise<number> { return new Promise((resolve) => { const start = Date.now() socket.emit('ping', () => { resolve(Date.now() - start) }) }) } }
八、安全與優(yōu)化
8.1 安全防護措施
- 傳輸加密:啟用WSS協(xié)議
- 請求驗證:
// 服務端中間件 const authMiddleware = (socket, next) => { try { const token = socket.handshake.auth.token const decoded = jwt.verify(token, SECRET_KEY) socket.user = decoded next() } catch (error) { next(new Error('認證失敗')) } }
- DDOS防護:限制連接頻率
const limiter = rateLimit({ windowMs: 60 * 1000, // 1分鐘 max: 100 // 最大連接數(shù) })
8.2 性能優(yōu)化策略
- 二進制傳輸優(yōu)化:
// 使用MessagePack替代JSON socket.emit('binary-data', msgpack.encode(largeData))
- 心跳檢測機制:
// 服務端設置 io.set('heartbeat interval', 5000) io.set('heartbeat timeout', 10000)
- 集群部署:
# 使用Redis適配器 npm install @socket.io/redis-adapter
九、項目部署方案
9.1 生產(chǎn)環(huán)境架構(gòu)
9.2 Nginx配置示例
# WebSocket代理配置 map $http_upgrade $connection_upgrade { default upgrade; '' close; } server { listen 443 ssl; server_name example.com; location /socket.io/ { proxy_pass http://ws_backend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_set_header Host $host; } }
十、效果演示與驗證
10.1 測試場景設計
- 正常傳輸測試:上傳2GB文件觀察實時數(shù)據(jù)
- 網(wǎng)絡中斷測試:斷開網(wǎng)絡后恢復觀察續(xù)傳
- 多并發(fā)測試:同時上傳10個文件觀察性能
- 極限測試:模擬1%丟包率環(huán)境
10.2 監(jiān)控指標驗證
指標 | 預期結(jié)果 |
---|---|
進度更新頻率 | ≤500ms |
速度計算誤差 | ≤5% |
斷線重連時間 | ≤3s |
CPU占用率 | ≤30% |
十一、總結(jié)與展望
11.1 實現(xiàn)成果
- 完整的實時文件傳輸監(jiān)控系統(tǒng)
- 企業(yè)級WebSocket應用架構(gòu)
- 生產(chǎn)環(huán)境部署方案
- 全面的異常處理機制
11.2 未來擴展方向
- 集成P2P傳輸協(xié)議
- 添加AI預測傳輸時間
- 實現(xiàn)跨設備同步傳輸
- 開發(fā)移動端適配版本
相關(guān)文章
vue實現(xiàn)盒子內(nèi)拖動方塊移動的示例代碼
這篇文章主要給大家介紹了如何通過vue實現(xiàn)盒子內(nèi)拖動方塊移動,文章通過代碼示例講解的非常詳細,具有一定的參考價值,感興趣的小伙伴可以參考閱讀本文2023-08-08使用Vue3和Pinia實現(xiàn)網(wǎng)頁刷新功能
在現(xiàn)代 Web 開發(fā)中,保持用戶界面的動態(tài)性和響應性至關(guān)重要,當用戶觸發(fā)某些操作時,例如點擊按鈕或者完成表單提交,我們往往需要刷新頁面的一部分來展示最新的數(shù)據(jù),本文將介紹如何使用 Vue 3 和 Pinia 來實現(xiàn)這一功能,需要的朋友可以參考下2024-08-08