前端长连接实现方案总结
以下是前端实现实时通信的常见长连接技术,按性能和复杂度递增排序:
一、短轮询(Short Polling)
- 原理 :前端通过定时器(如
setInterval
)周期性发送 HTTP 请求,服务器立即响应(无论有无新数据)。 - 优点:实现简单,浏览器兼容性高。
- 缺点:频繁请求浪费带宽和服务器资源,实时性差(延迟取决于轮询间隔)。
- 适用场景:数据更新频率低且实时性要求不高的场景(如简单状态监控)。
二、长轮询(Long Polling / Comet)
- 原理:前端发起请求后,服务器保持连接直到有新数据或超时,响应后客户端立即重新发起请求。
- 优点:相比短轮询减少无效请求,实时性提升。
- 缺点:服务器需维护挂起连接,高并发时资源压力大。
- 适用场景:中等实时性需求(如简易聊天室)。
三、HTTP 长连接(Keep-Alive)
- 原理 :通过 HTTP/1.1 的
Connection: keep-alive
复用 TCP 连接,支持多个请求复用同一连接。 - 优点:减少 TCP 握手次数,提升传输效率。
- 缺点:仍基于请求-响应模式,无法实现服务端主动推送。
- 适用场景:需要减少连接开销的常规 HTTP 请求。
四、Server-Sent Events(SSE)
- 原理 :基于 HTTP 的单向长连接,服务端通过
EventSource
接口主动推送数据到前端。 - 优点:支持自动重连、轻量级协议,兼容性较好。
- 缺点:仅支持服务端到客户端的单向通信。
- 适用场景:实时数据推送(如新闻推送、股票行情)。
五、WebSocket
- 原理:基于 TCP 的全双工协议,通过一次握手建立持久连接,支持客户端和服务端双向实时通信。
- 优点:低延迟、高吞吐量,支持二进制和文本数据。
- 缺点:需额外处理连接状态(如重连、心跳检测),旧浏览器需降级方案。
- 适用场景:高实时性应用(如在线游戏、实时协作工具)。
六、方案对比
方案 | 实时性 | 带宽消耗 | 双向通信 | 适用场景 |
---|---|---|---|---|
短轮询 | 低 | 高 | 否 | 低频数据查询 |
长轮询 | 中 | 中 | 否 | 中等实时性需求 |
HTTP 长连接 | 低 | 低 | 否 | 高频次常规请求 |
SSE | 高 | 低 | 单向 | 服务端单向推送 |
WebSocket | 极高 | 低 | 是 | 全双工实时交互 |
一、为什么需要切片上传?
-
解决大文件传输痛点
- 网络稳定性:避免因单次传输失败导致整个文件重传。
- 性能优化:分片并行上传可充分利用带宽,缩短总耗时。
- 断点续传:记录已上传分片,支持中断后恢复上传。
- 服务器兼容性:绕过服务器对单文件大小的限制(如 Nginx 默认限制 1MB)。
二、切片上传的核心实现步骤
1. 文件分片(File Chunking)
- 前端切片方式:
ini
const chunkSize = 2 * 1024 * 1024; // 每片 2MB
const chunks = [];
let start = 0;
while (start < file.size) {
const chunk = file.slice(start, start + chunkSize);
chunks.push(chunk);
start += chunkSize;
}
-
分片大小选择:
- 过小(如 100KB):分片过多,增加请求和管理开销。
- 过大(如 10MB):失去分片意义,难以断点续传。
- 建议:1~5MB,根据实际网络条件动态调整。
2. 分片唯一标识(Hash)
-
生成文件唯一标识:
-
使用
文件名 + 文件大小 + 最后修改时间
作为标识(简单但可能冲突)。 -
计算文件哈希(更可靠,但耗时):
-
csharp
const hash = await calculateFileHash(file); // 使用 SparkMD5 等库
-
分片命名规则:
- 格式:
{hash}-{index}
(如a3c8b1d2-0
)。 - 服务端通过
hash
合并所有分片。
- 格式:
3. 并发控制(Concurrency Control)
- 浏览器并发限制:Chrome 同域名限制 6 个并发请求。
- 实现并发队列:
javascript
const maxConcurrency = 3; // 控制并发数
const uploadChunk = async (chunk, index) => { /* 上传逻辑 */ };
// 使用 Promise.all 控制并发
const chunksToUpload = chunks.map((chunk, index) => ({ chunk, index }));
while (chunksToUpload.length > 0) {
const batch = chunksToUpload.splice(0, maxConcurrency);
await Promise.all(batch.map(({ chunk, index }) => uploadChunk(chunk, index)));
}
4. 断点续传(Resumable Upload)
-
前端记录已上传分片:
- 使用
localStorage
或IndexedDB
存储已上传的分片索引。
- 使用
-
服务端校验分片状态:
- 上传前先请求接口检查哪些分片已上传(根据分片
hash
)。 - 示例接口:
- 上传前先请求接口检查哪些分片已上传(根据分片
ini
// 请求:GET /upload/check?hash=a3c8b1d2
// 响应:{ uploadedChunks: [0, 1, 2] }
5. 分片上传与合并
-
分片上传接口:
- 使用
FormData
上传分片数据:
- 使用
go
const formData = new FormData();
formData.append('chunk', chunk);
formData.append('hash', hash);
formData.append('index', index);
await axios.post('/upload/chunk', formData);
服务端合并文件:
- 按分片索引顺序合并所有分片(避免乱序导致文件损坏)。
- 示例(Node.js):
ini
const mergeChunks = (hash, totalChunks) => {
const chunkPaths = Array.from({ length: totalChunks }, (_, i) => `./temp/${hash}-${i}`);
const writeStream = fs.createWriteStream(`./files/${hash}`);
chunkPaths.forEach(path => {
const data = fs.readFileSync(path);
writeStream.write(data);
fs.unlinkSync(path); // 删除临时分片
});
writeStream.end();
};
6. 错误处理与重试机制
-
分片上传失败重试:
- 为每个分片设置重试次数(如 3 次),采用指数退避策略。
- 示例:
javascript
const uploadWithRetry = async (chunk, index, retries = 3) => {
try {
await uploadChunk(chunk, index);
} catch (err) {
if (retries > 0) {
await new Promise(resolve => setTimeout(resolve, 1000 * (4 - retries)));
return uploadWithRetry(chunk, index, retries - 1);
} else {
throw err;
}
}
};
-
全局错误监控:
- 监听
unhandledrejection
事件捕获未处理的 Promise 错误。
- 监听
7. 上传进度监控
- 计算总进度:
ini
let uploadedSize = 0;
const totalSize = file.size;
// 每个分片上传成功后更新进度
uploadedSize += chunk.size;
const progress = Math.round((uploadedSize / totalSize) * 100);
-
实时展示进度条:
- 使用第三方库(如
axios
的onUploadProgress
)或自定义事件。
- 使用第三方库(如
三、扩展优化点(加分项)
- 动态分片大小:根据网络速度调整分片大小(如高速网络用 5MB,低速用 1MB)。
- 文件秒传:服务端通过文件哈希判断是否已存在相同文件,直接跳过上传。
- 浏览器空闲时段上传 :使用
requestIdleCallback
在空闲时上传分片。 - 压缩分片 :对图片/视频分片进行压缩(如
canvas.toBlob()
)。 - 分片哈希校验:上传分片后,服务端校验分片哈希,防止传输损坏。
"大文件切片上传的核心目标是提升传输可靠性和用户体验。关键点包括:
- 合理分片 :根据文件大小动态分片(通常 1-5MB),利用
Blob.slice
切割文件; - 唯一标识:通过文件哈希或元数据标识分片,确保服务端正确合并;
- 并发控制:限制并行请求数,避免触发浏览器并发限制;
- 断点续传:结合本地存储和服务端校验,实现中断后快速恢复;
- 错误重试:对失败分片自动重试,采用指数退避策略;
- 分片校验 :前后端协同验证分片完整性(如哈希比对)。
此外,还可以优化分片大小、实现秒传和动态进度提示等。"