基于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),需要的朋友可以參考下
一、需求分析與技術選型
1.1 實時監(jiān)控系統(tǒng)核心需求
- 實時顯示文件傳輸進度
- 動態(tài)更新傳輸速度/剩余時間
- 多任務并行狀態(tài)管理
- 異常中斷實時告警
- 歷史傳輸記錄可視化
1.2 技術架構設計

1.3 技術棧說明
- 前端: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) // 鑒權邏輯
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)調整分片大小
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 傳輸質量監(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 生產環(huán)境架構

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)控指標驗證
| 指標 | 預期結果 |
|---|---|
| 進度更新頻率 | ≤500ms |
| 速度計算誤差 | ≤5% |
| 斷線重連時間 | ≤3s |
| CPU占用率 | ≤30% |
十一、總結與展望
11.1 實現(xiàn)成果
- 完整的實時文件傳輸監(jiān)控系統(tǒng)
- 企業(yè)級WebSocket應用架構
- 生產環(huán)境部署方案
- 全面的異常處理機制
11.2 未來擴展方向
- 集成P2P傳輸協(xié)議
- 添加AI預測傳輸時間
- 實現(xiàn)跨設備同步傳輸
- 開發(fā)移動端適配版本
相關文章
使用Vue3和Pinia實現(xiàn)網(wǎng)頁刷新功能
在現(xiàn)代 Web 開發(fā)中,保持用戶界面的動態(tài)性和響應性至關重要,當用戶觸發(fā)某些操作時,例如點擊按鈕或者完成表單提交,我們往往需要刷新頁面的一部分來展示最新的數(shù)據(jù),本文將介紹如何使用 Vue 3 和 Pinia 來實現(xiàn)這一功能,需要的朋友可以參考下2024-08-08

