基于Vue3+WebSocket實(shí)現(xiàn)實(shí)時(shí)文件傳輸監(jiān)控系統(tǒng)
更新時(shí)間:2025年03月26日 10:20:45 作者:北辰alk
WebSocket是一種在客戶端和服務(wù)器之間進(jìn)行雙向通信的網(wǎng)絡(luò)協(xié)議,它通過建立持久性的、全雙工的連接,允許服務(wù)器主動(dòng)向客戶端發(fā)送數(shù)據(jù),本文小編給大家介紹了基于Vue3+WebSocket實(shí)現(xiàn)實(shí)時(shí)文件傳輸監(jiān)控系統(tǒng),需要的朋友可以參考下
一、需求分析與技術(shù)選型
1.1 實(shí)時(shí)監(jiān)控系統(tǒng)核心需求
- 實(shí)時(shí)顯示文件傳輸進(jìn)度
- 動(dòng)態(tài)更新傳輸速度/剩余時(shí)間
- 多任務(wù)并行狀態(tài)管理
- 異常中斷實(shí)時(shí)告警
- 歷史傳輸記錄可視化
1.2 技術(shù)架構(gòu)設(shè)計(jì)
1.3 技術(shù)棧說明
- 前端:Vue3 + Element Plus + ECharts
- 實(shí)時(shí)通信:WebSocket + Socket.IO
- 后端:Node.js + Express + WebSocket
- 數(shù)據(jù)存儲(chǔ):MongoDB(歷史記錄)
- 輔助庫:dayjs(時(shí)間處理)、lodash(數(shù)據(jù)處理)
二、環(huán)境搭建與配置
2.1 創(chuàng)建Vue3項(xiàng)目
npm create vue@latest # 選擇TypeScript、Router、Pinia
2.2 安裝核心依賴
npm install socket.io-client @types/socket.io-client echarts element-plus
2.3 WebSocket服務(wù)端搭建
// 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}`) // 身份驗(yàn)證 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服務(wù)封裝
// 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錯(cuò)誤:', 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()
四、實(shí)時(shí)監(jiān)控界面開發(fā)
4.1 傳輸任務(wù)列表組件
<template> <el-table :data="tasks" style="width: 100%"> <el-table-column prop="fileName" label="文件名" /> <el-table-column label="進(jìn)度"> <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 實(shí)時(shí)速度圖表(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 增強(qiáng)版文件上傳器
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)絡(luò)速度動(dò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 速度計(jì)算算法
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 } }
六、服務(wù)端實(shí)時(shí)處理
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 }) // 廣播進(jìn)度更新 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 } }
七、高級(jí)功能實(shí)現(xiàn)
7.1 斷線自動(dòng)重連
// 前端重連邏輯 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 安全防護(hù)措施
- 傳輸加密:?jiǎn)⒂肳SS協(xié)議
- 請(qǐng)求驗(yàn)證:
// 服務(wù)端中間件 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('認(rèn)證失敗')) } }
- DDOS防護(hù):限制連接頻率
const limiter = rateLimit({ windowMs: 60 * 1000, // 1分鐘 max: 100 // 最大連接數(shù) })
8.2 性能優(yōu)化策略
- 二進(jìn)制傳輸優(yōu)化:
// 使用MessagePack替代JSON socket.emit('binary-data', msgpack.encode(largeData))
- 心跳檢測(cè)機(jī)制:
// 服務(wù)端設(shè)置 io.set('heartbeat interval', 5000) io.set('heartbeat timeout', 10000)
- 集群部署:
# 使用Redis適配器 npm install @socket.io/redis-adapter
九、項(xiàng)目部署方案
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; } }
十、效果演示與驗(yàn)證
10.1 測(cè)試場(chǎng)景設(shè)計(jì)
- 正常傳輸測(cè)試:上傳2GB文件觀察實(shí)時(shí)數(shù)據(jù)
- 網(wǎng)絡(luò)中斷測(cè)試:斷開網(wǎng)絡(luò)后恢復(fù)觀察續(xù)傳
- 多并發(fā)測(cè)試:同時(shí)上傳10個(gè)文件觀察性能
- 極限測(cè)試:模擬1%丟包率環(huán)境
10.2 監(jiān)控指標(biāo)驗(yàn)證
指標(biāo) | 預(yù)期結(jié)果 |
---|---|
進(jìn)度更新頻率 | ≤500ms |
速度計(jì)算誤差 | ≤5% |
斷線重連時(shí)間 | ≤3s |
CPU占用率 | ≤30% |
十一、總結(jié)與展望
11.1 實(shí)現(xiàn)成果
- 完整的實(shí)時(shí)文件傳輸監(jiān)控系統(tǒng)
- 企業(yè)級(jí)WebSocket應(yīng)用架構(gòu)
- 生產(chǎn)環(huán)境部署方案
- 全面的異常處理機(jī)制
11.2 未來擴(kuò)展方向
- 集成P2P傳輸協(xié)議
- 添加AI預(yù)測(cè)傳輸時(shí)間
- 實(shí)現(xiàn)跨設(shè)備同步傳輸
- 開發(fā)移動(dòng)端適配版本
相關(guān)文章
vue實(shí)現(xiàn)盒子內(nèi)拖動(dòng)方塊移動(dòng)的示例代碼
這篇文章主要給大家介紹了如何通過vue實(shí)現(xiàn)盒子內(nèi)拖動(dòng)方塊移動(dòng),文章通過代碼示例講解的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴可以參考閱讀本文2023-08-08在 Vue 應(yīng)用中使用 Netlify 表單功能的方法詳解
Netlify 帶有內(nèi)置表單處理功能,可以用來存儲(chǔ)表單數(shù)據(jù),下載 csv 文件,同時(shí)可以在接收到新的提交時(shí)發(fā)送郵件通知或者通過配置 webhook 發(fā)送請(qǐng)求。這篇文章主要介紹了在 Vue 應(yīng)用中使用 Netlify 表單功能,需要的朋友可以參考下2019-06-06使用Vue3和Pinia實(shí)現(xiàn)網(wǎng)頁刷新功能
在現(xiàn)代 Web 開發(fā)中,保持用戶界面的動(dòng)態(tài)性和響應(yīng)性至關(guān)重要,當(dāng)用戶觸發(fā)某些操作時(shí),例如點(diǎn)擊按鈕或者完成表單提交,我們往往需要刷新頁面的一部分來展示最新的數(shù)據(jù),本文將介紹如何使用 Vue 3 和 Pinia 來實(shí)現(xiàn)這一功能,需要的朋友可以參考下2024-08-08vue 項(xiàng)目接口管理的實(shí)現(xiàn)
在vue開發(fā)中,會(huì)涉及到很多接口的處理,當(dāng)項(xiàng)目足夠大時(shí),就需要定義規(guī)范統(tǒng)一的接口,本文就來介紹一下vue 項(xiàng)目接口管理,具有一定的參考價(jià)值,感興趣的小伙伴可以一起來了解一下2019-01-01