从云盘存储到 WebRTC 在线传输:一体化文件平台的功能与技术架构设计
从云盘存储到 WebRTC 在线传输:一体化文件平台的功能与技术架构设计
前言
在开发文件管理类产品时,最初的需求往往很简单:
用户上传一个文件,然后在其他设备上下载。
但随着使用场景增加,系统很快就会面临更多问题:
-
有些文件需要长期保存;
-
有些文件只需要临时发送一次;
-
有些用户希望限制下载次数;
-
有些文件需要自动过期;
-
有些场景需要让别人向自己提交文件;
-
有些用户希望电脑和手机之间直接传输;
-
开发者还可能需要分发 APK 或 IPA 安装包;
-
涉及证件、合同等文件时,还需要更严格的隐私保护。
如果每个需求都使用一个独立工具,文件会分散在多个平台中,用户也需要反复注册、上传、下载和整理。
基于这些实际场景,我们设计了一套一体化文件平台,将云盘存储、文件快传、在线传输、文件收集、笔记、便签、隐私空间和应用分发整合在同一个系统中。
本文主要从产品设计和技术实现两个角度,介绍一套综合型文件平台应当如何划分业务模块,以及不同传输方式之间应该如何配合。
一、为什么云盘不能只解决“存文件”
传统云盘的核心流程通常是:
选择文件
↓
上传到服务器
↓
保存到对象存储
↓
生成下载地址
↓
接收方下载
这套流程适合需要长期保存的文件,但并不适合所有场景。
例如,用户只是想把一个视频从电脑传到手机。如果仍然要求用户:
-
登录云盘;
-
上传文件;
-
等待上传完成;
-
在手机登录;
-
下载文件;
-
再删除云端文件;
整个过程就比较繁琐。
因此,在文件平台设计中,应该根据文件的生命周期和使用目的,将业务拆分为多个模式。
二、文件业务的四种典型模式
一套完整的文件平台,通常至少需要覆盖以下四种模式。
1. 长期存储
适用于:
-
个人资料备份;
-
图片和视频保存;
-
工作文档归档;
-
多设备同步访问;
-
项目文件长期管理。
这类文件应进入正式云盘目录,并支持:
-
文件夹分类;
-
重命名;
-
移动;
-
收藏;
-
分享;
-
回收站;
-
权限管理。
2. 临时快传
适用于:
-
临时发送压缩包;
-
给客户发送一次性文件;
-
分享短期有效资料;
-
限制文件下载次数。
这类文件不一定需要长期进入用户云盘,而是应该支持自动过期。
3. 实时在线传输
适用于:
-
电脑向手机传文件;
-
两台电脑之间互传文件;
-
双方同时在线;
-
不希望文件长期存储在服务器。
这种场景可以优先使用 WebRTC 建立点对点连接。
4. 文件收集
适用于:
-
教师收作业;
-
企业收员工资料;
-
项目负责人收集协作文件;
-
摄影师收集客户素材;
-
活动方收集报名附件。
这种模式的方向和普通分享相反。
普通分享是:
我把文件发给别人
文件收集则是:
别人把文件提交给我
这两种模式在权限设计、目录管理和页面逻辑上完全不同。
三、整体系统架构
一体化文件平台可以拆分为以下几层:
用户端
├── Web 浏览器
├── 手机浏览器
└── 管理控制台
业务层
├── 云盘存储
├── 文件分享
├── 文件快传
├── 在线传输
├── 文件收集
├── 云笔记
├── 云便签
├── 隐私空间
└── 应用内测分发
服务层
├── 用户服务
├── 文件服务
├── 上传服务
├── 下载服务
├── 分享服务
├── WebSocket 信令服务
├── 权限验证服务
└── 任务调度服务
基础设施层
├── 对象存储
├── CDN
├── 数据库
├── Redis
├── STUN/TURN
└── 日志与监控
不同业务模块可以共用底层文件服务,但在权限、生命周期和访问方式上保持独立。
四、云盘存储模块设计
云盘存储是整个系统的基础。
1. 文件与目录数据结构
数据库中通常不直接保存文件内容,而是保存文件元数据。
例如:
CREATE TABLE files (
id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL,
parent_id BIGINT DEFAULT 0,
file_name VARCHAR(255) NOT NULL,
storage_key VARCHAR(512),
file_size BIGINT DEFAULT 0,
mime_type VARCHAR(128),
file_hash VARCHAR(128),
is_folder TINYINT DEFAULT 0,
status TINYINT DEFAULT 1,
created_at DATETIME,
updated_at DATETIME
);
其中:
-
parent_id用于实现多级目录; -
storage_key对应对象存储中的真实路径; -
file_hash可以用于秒传和重复文件检测; -
status可以区分正常、回收站和彻底删除状态; -
文件夹本身不需要对应对象存储文件。
2. 文件内容与元数据分离
文件内容保存在对象存储中,数据库只保存索引信息。
这样可以避免:
-
数据库体积过大;
-
数据库备份困难;
-
大文件读写阻塞;
-
应用服务器磁盘不足。
典型流程如下:
浏览器申请上传凭证
↓
服务端验证用户权限和空间
↓
浏览器直接上传对象存储
↓
对象存储返回结果
↓
服务端写入文件元数据
相比文件先经过业务服务器,再上传到对象存储,客户端直传可以减少应用服务器的带宽压力。
五、分片上传与大文件处理
大文件不能始终使用普通单请求上传。
如果上传一个数GB文件时网络发生短暂中断,用户不应该从头重新上传。
因此需要实现分片上传。
1. 分片上传流程
创建上传任务
↓
文件切片
↓
并发上传多个分片
↓
记录成功分片
↓
失败分片重试
↓
服务端或对象存储合并
↓
写入完整文件记录
前端切片示例:
function createChunks(file, chunkSize = 10 * 1024 * 1024) {
const chunks = [];
let start = 0;
let index = 0;
while (start < file.size) {
const end = Math.min(start + chunkSize, file.size);
chunks.push({
index,
blob: file.slice(start, end),
start,
end
});
start = end;
index += 1;
}
return chunks;
}
2. 断点续传
服务端应该保存已经上传成功的分片编号。
用户重新打开页面后,可以先查询任务状态:
{
"uploadId": "upload_123456",
"uploadedParts": [1, 2, 3, 5, 6]
}
客户端只上传缺少的第4片以及后续分片。
3. 文件哈希
可以通过文件哈希实现:
-
文件完整性校验;
-
重复文件识别;
-
快速上传;
-
分片校验。
但浏览器对超大文件计算完整哈希可能比较耗时,因此可以采用:
-
Web Worker计算;
-
抽样哈希;
-
首尾分片加文件大小;
-
后台异步完整校验。
六、文件快传与生命周期控制
文件快传和普通云盘上传最大的区别,是文件具有明确的失效条件。
例如:
-
保存24小时;
-
保存12小时;
-
保存6小时;
-
保存60分钟;
-
下载100次后失效;
-
下载10次后失效;
-
下载1次后失效;
-
接收后立即删除。
1. 快传任务表
CREATE TABLE transfer_tasks (
id BIGINT PRIMARY KEY,
user_id BIGINT,
transfer_code VARCHAR(64) UNIQUE,
storage_key VARCHAR(512) NOT NULL,
file_name VARCHAR(255),
file_size BIGINT,
expire_at DATETIME,
max_downloads INT DEFAULT 1,
download_count INT DEFAULT 0,
status TINYINT DEFAULT 1,
created_at DATETIME
);
下载前需要同时判断:
当前时间是否超过 expire_at
下载次数是否达到 max_downloads
任务状态是否有效
文件对象是否仍然存在
验证示例:
function canDownload(task) {
if (task.status !== 1) {
return false;
}
if (new Date() >= new Date(task.expireAt)) {
return false;
}
if (task.downloadCount >= task.maxDownloads) {
return false;
}
return true;
}
2. 自动清理机制
文件过期后不能只在数据库中标记失效,还应定期删除对象存储中的实际文件。
可以使用定时任务:
每5分钟扫描过期任务
↓
更新任务状态
↓
删除对象存储文件
↓
记录清理日志
任务量较大时,可以将过期时间写入延迟队列,而不是频繁全表扫描。
七、基于 WebRTC 的在线传输
对于双方同时在线的传输场景,可以使用 WebRTC DataChannel。
其基本架构包括:
发送端浏览器
│
│ WebSocket 信令
▼
信令服务器
▲
│ WebSocket 信令
│
接收端浏览器
发送端 ←── WebRTC DataChannel ──→ 接收端
信令服务器只交换:
-
Offer;
-
Answer;
-
ICE Candidate;
-
房间状态;
-
连接状态。
文件数据尽量不经过信令服务器。
1. 创建连接
const peerConnection = new RTCPeerConnection({
iceServers: [
{
urls: "stun:stun.example.com:3478"
},
{
urls: [
"turn:turn.example.com:3478?transport=udp",
"turn:turn.example.com:3478?transport=tcp"
],
username: "temporary-user",
credential: "temporary-password"
}
]
});
2. 创建 DataChannel
const channel = peerConnection.createDataChannel("file-transfer", {
ordered: true
});
文件传输一般使用可靠、有序模式。
3. 文件分片发送
async function sendFile(file, channel) {
const chunkSize = 64 * 1024;
let offset = 0;
while (offset < file.size) {
if (channel.readyState !== "open") {
throw new Error("DataChannel 已关闭");
}
while (channel.bufferedAmount > 1024 * 1024) {
await new Promise(resolve => setTimeout(resolve, 50));
}
const chunk = file.slice(offset, offset + chunkSize);
const buffer = await chunk.arrayBuffer();
channel.send(buffer);
offset += buffer.byteLength;
}
}
八、为什么 WebRTC 仍然需要 TURN
不少开发者第一次实现 WebRTC 时,只配置了 STUN。
在局域网或部分家庭宽带环境中可以正常连接,但换成:
-
移动网络;
-
企业网络;
-
校园网;
-
对称型 NAT;
-
严格防火墙;
连接就可能失败。
STUN只能帮助客户端发现公网映射地址,并不能解决所有NAT穿透问题。
因此,生产环境通常还需要部署TURN服务器。
当P2P直连失败时,文件数据会通过TURN中继:
发送端 → TURN服务器 → 接收端
此时连接成功率会更高,但服务器带宽成本也会增加。
因此系统可以采用以下策略:
-
优先尝试P2P直连;
-
直连失败后切换TURN;
-
界面提示当前传输模式;
-
对TURN任务设置合理带宽和频率限制。
九、文件收集模块的权限设计
文件收集不能直接开放用户的云盘目录权限。
合理的设计应该是:
创建者建立收集任务
↓
指定接收目录
↓
生成独立收集链接
↓
提交者打开上传页面
↓
提交者只能上传
↓
无法查看其他文件
↓
文件自动归档到指定目录
收集任务表可以包含:
CREATE TABLE collection_tasks (
id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL,
title VARCHAR(255),
target_folder_id BIGINT NOT NULL,
collection_code VARCHAR(64) UNIQUE,
require_name TINYINT DEFAULT 1,
expire_at DATETIME,
status TINYINT DEFAULT 1,
created_at DATETIME
);
上传记录单独保存:
CREATE TABLE collection_submissions (
id BIGINT PRIMARY KEY,
collection_id BIGINT NOT NULL,
submitter_name VARCHAR(128),
file_id BIGINT NOT NULL,
ip_address VARCHAR(64),
created_at DATETIME
);
这样可以实现:
-
查看谁提交了文件;
-
统计提交数量;
-
限制单次文件数量;
-
限制文件类型;
-
阻止匿名滥用;
-
对恶意上传进行追踪。
十、云笔记与文件系统的关联
很多文件平台只存储文件,但用户实际还需要记录文件相关信息。
例如:
-
项目需求;
-
文件说明;
-
修改记录;
-
客户反馈;
-
待办事项。
因此,笔记模块可以和云盘文件建立关联关系。
CREATE TABLE note_attachments (
id BIGINT PRIMARY KEY,
note_id BIGINT NOT NULL,
file_id BIGINT NOT NULL,
created_at DATETIME
);
用户在笔记中插入附件时,不需要重复上传,而是直接关联已有云盘文件。
这样可以避免:
-
同一文件保存多份;
-
占用重复空间;
-
修改后多个副本不一致。
云便签则更偏向短文本和待办事项,可以采用更轻量的数据结构。
十一、隐私空间的设计思路
隐私空间不能只理解为一个普通文件夹。
至少应该在以下方面与普通文件区分:
-
独立访问验证;
-
独立权限判断;
-
文件加密;
-
操作日志;
-
敏感操作二次验证;
-
分享能力限制。
一种实现方式是对文件内容进行客户端加密,再将密文上传到对象存储。
基本流程:
用户输入隐私空间密码
↓
本地派生加密密钥
↓
浏览器加密文件
↓
上传密文
↓
下载后本地解密
这种方式可以降低服务端直接读取明文的可能性。
但需要特别注意:
-
用户忘记密钥后可能无法恢复;
-
大文件加解密性能;
-
浏览器内存限制;
-
密钥不能明文存储在服务端;
-
密钥派生需要使用安全算法;
-
不应自行设计未经验证的加密协议。
实际项目中可以采用 Web Crypto API,并选择成熟的加密模式。
十二、应用内测分发模块
应用分发的本质是特殊类型的文件托管和下载页面。
相较普通文件分享,还需要额外处理:
-
APK信息解析;
-
IPA信息解析;
-
应用名称;
-
应用图标;
-
版本号;
-
包名;
-
更新说明;
-
下载次数;
-
历史版本;
-
分发状态。
解析后可以生成应用页面:
应用图标
应用名称
当前版本
文件大小
更新时间
更新说明
下载安装按钮
对于Android APK,可以在服务端解析:
-
packageName -
versionName -
versionCode -
应用图标
-
最低系统版本
对于IPA,由于签名和安装机制更复杂,还需要根据实际分发方式处理描述文件、签名状态和安装链接。
应用分发不能简单等同于普通文件下载,还需要做好:
-
文件合法性验证;
-
恶意文件检测;
-
上传者身份验证;
-
下载频率限制;
-
内容审核;
-
投诉举报机制。
十三、多节点与CDN设计
当用户分布在不同地区时,单一存储节点可能导致访问延迟较高。
可以根据业务情况部署多个区域节点,例如:
-
中国大陆;
-
亚洲;
-
北美;
-
欧洲。
上传时可以让用户选择目标区域,或者根据用户IP自动推荐。
节点选择策略可以综合考虑:
用户所在地区
文件主要下载地区
节点实时负载
节点存储容量
跨区域流量成本
合规要求
对于公开分享和应用分发,可以使用CDN缓存下载内容。
但对于以下文件,应谨慎缓存:
-
隐私空间文件;
-
一次性下载文件;
-
权限敏感文件;
-
带动态鉴权的文件。
下载地址应使用短期签名URL,而不是长期公开对象地址。
十四、下载鉴权与防盗链
如果对象存储地址直接暴露,用户可能绕过业务系统访问文件。
常见解决方案包括:
-
签名URL;
-
下载Token;
-
Referer防盗链;
-
IP限制;
-
下载次数验证;
-
有效时间限制。
下载流程可以设计为:
用户请求下载
↓
服务端验证登录状态
↓
验证文件权限
↓
验证有效期和下载次数
↓
生成短期签名地址
↓
浏览器跳转下载
签名地址的有效期不宜过长。
例如:
5分钟
10分钟
30分钟
具体取决于文件大小和下载场景。
十五、文件平台的安全风险
文件上传类系统必须重点关注安全问题。
1. 任意文件上传
不能仅根据文件扩展名判断类型。
需要结合:
-
MIME类型;
-
文件头;
-
扩展名;
-
内容扫描;
-
解压检测。
2. XSS文件名
用户上传的文件名可能包含HTML或脚本内容。
展示时必须转义。
3. 恶意压缩包
压缩包可能存在:
-
超高压缩比;
-
嵌套压缩;
-
路径穿越;
-
恶意脚本;
-
病毒文件。
4. 下载接口刷流量
攻击者可能反复请求大文件,造成高额带宽消耗。
需要设置:
-
单IP限速;
-
单账号限速;
-
单链接下载次数;
-
异常流量监控;
-
CDN防盗链。
5. WebRTC连接码枚举
在线传输的连接码不能过短。
建议:
-
使用足够长度的随机字符串;
-
设置短时间有效期;
-
限制错误尝试次数;
-
成功连接后立即失效;
-
使用临时会话Token。
十六、为什么要把多种功能放在同一个平台
单独实现云盘、快传、P2P传输或文件收集并不困难。
真正有价值的是让不同模块形成连续的文件工作流。
例如,一个项目可以按照以下流程处理:
通过文件收集获取客户资料
↓
文件自动进入云盘目录
↓
使用云笔记记录项目需求
↓
使用云便签记录临时待办
↓
通过在线传输在设备间移动素材
↓
通过文件快传发送临时版本
↓
通过云盘分享交付正式文件
↓
敏感合同进入隐私空间
↓
如果是App项目,再创建内测分发
这类整合的价值不只是功能数量,而是减少文件在多个平台之间反复上传和迁移。
十七、实际项目中的取舍
在真实开发中,没有一种文件传输方式适合所有场景。
对象存储适合
-
长期保存;
-
一对多下载;
-
接收方不在线;
-
CDN加速;
-
历史文件管理。
文件快传适合
-
短期保存;
-
限制下载次数;
-
自动过期;
-
一次性交付。
WebRTC适合
-
双方同时在线;
-
设备之间互传;
-
不需要长期保存;
-
希望减少中心服务器流量。
TURN适合
-
P2P无法连接;
-
企业网络或复杂NAT;
-
需要提高连接成功率。
文件收集适合
-
多人向一个用户提交文件;
-
文件自动归档;
-
避免聊天工具中资料分散。
因此,合理的系统不应该强制所有业务使用同一种传输方式,而应该根据文件生命周期和网络环境动态选择。
十八、总结
一个现代文件平台需要解决的问题,早已不只是“上传和下载”。
它还需要处理:
-
长期存储;
-
临时传输;
-
点对点在线传输;
-
文件收集;
-
多设备访问;
-
文件分享;
-
隐私保护;
-
应用内测分发;
-
多区域节点;
-
生命周期管理;
-
下载鉴权;
-
安全审核。
从技术角度看,对象存储、CDN、WebSocket、WebRTC、STUN、TURN、数据库和任务调度各自承担不同职责。
从产品角度看,最重要的是根据用户真实场景,将这些能力组合成一条完整的文件工作流。
本文所介绍的功能和架构,已经应用在实际文件平台项目中。感兴趣的开发者可以访问项目官网了解相关功能:
后续还可以继续深入讨论以下内容:
-
基于Node.js搭建WebRTC信令服务器;
-
Coturn服务器部署与配置;
-
大文件分片上传和断点续传;
-
对象存储直传与签名URL;
-
文件收集系统的数据库设计;
-
WebRTC大文件传输的内存优化。
更多推荐

所有评论(0)