基于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-08
vue 項(xiàng)目接口管理的實(shí)現(xiàn)
在vue開發(fā)中,會(huì)涉及到很多接口的處理,當(dāng)項(xiàng)目足夠大時(shí),就需要定義規(guī)范統(tǒng)一的接口,本文就來介紹一下vue 項(xiàng)目接口管理,具有一定的參考價(jià)值,感興趣的小伙伴可以一起來了解一下2019-01-01

