让你设计一个文件上传系统,怎么设计?
- 标签:后端、系统设计、场景题
🏛 业务架构师
文件上传是典型的**"业务量级决定架构"**的题,我会先问:日上传量多大?文件类型?是用户头像还是商户资质还是视频内容?
第一档:中小规模(绝大多数业务)——前端直传对象存储
后端只做两件事:签发带时效的上传凭证(Policy/STS),接收 OSS 回调确认落库。文件流量完全不经过自家服务器,一台应用服务器的带宽都不用为上传准备,成本最低、可用性最高(对象存储 11 个 9 的持久性)。我会坚持这个方案,因为自建上传服务在这档是纯浪费钱。
第二档:体验增强
- 分片上传 + 断点续传:大于 50MB 的文件(视频、安装包)必备,弱网环境上传成功率直接决定内容供给量——这是内容平台的供给转化指标。
- 秒传:前端算文件 MD5,服务端命中已存文件直接返回地址,网盘类产品的存储成本杀手。
第三档:平台级(网盘/视频平台)
- 上传加速节点、就近接入
- 异步转码流水线(视频抽帧、多码率)
- 内容安全审核前置(色情/版权识别),合规风险必须拦在入库前
业务指标与风控
- 核心看上传成功率和上传耗时,失败即用户流失。
- 必须做文件类型白名单 + 大小限制 + 病毒扫描,否则对象存储会变成违法内容的免费托管所,这是法律风险不是技术风险。
- 存储成本要设单用户配额,否则羊毛党能把存储账单刷爆——存储费是持续资损。
我的总结:上传系统的架构复杂度应该和"文件对业务的价值密度"成正比,交易凭证类从严审核,社交内容类从快从简加异步审核。
🏗 系统架构师
文件上传系统的核心矛盾是:上传流量不应该穿过应用服务器。我会按这个原则分层设计。
接入层:直传对象存储
- 客户端先调业务接口申请预签名 URL(带过期时间和文件大小、类型约束),然后分片直传 OSS/S3,后端只做签名不落流量。一台应用服务器扛不了多少上传带宽(千兆网卡约 100MB/s),把带宽成本转给对象存储 + CDN 是唯一可扩展路径。
- 前端分片:大文件按 5~10MB 切片并行上传,单片失败只重试该片;记录已传分片实现断点续传。
秒传与去重 上传前计算文件 MD5/SHA-256,服务端查 hash 索引(Redis bloom + DB),命中则直接登记元数据返回成功——网盘场景可省 30% 以上存储和带宽。
合并与回调 全部分片完成后触发合并:小规模用 OSS 的 compose/分片上传 API 原生合并;需要病毒扫描、转码等后处理的,走 MQ 异步任务队列,避免阻塞上传链路。
数据模型与安全
- 元数据表:
文件hash、大小、MIME、上传者、存储key、状态,hash 唯一索引支撑秒传。 - 安全红线:服务端二次校验真实文件类型(魔数而非扩展名)、图片重编码剥掉恶意载荷、私有 bucket + 短时效下载签名、按用户配额限流。
量级分档:日均千级文件 Nginx 本地盘 + 定期归档即可;日均百万级必须对象存储直传 + CDN 分发,应用层只保留元数据和控制面。
💻 全栈开发专家
我做过网盘类项目,核心诉求是:大文件不占后端带宽、支持断点续传、秒传、可靠落盘。我的设计:
整体链路 前端分片 → 后端签发预签名 URL → 直传对象存储(OSS/MinIO)→ 通知后端合并 → 落库元数据。
1. 秒传 + 断点续传(体验关键) 上传前先算整文件 MD5,请求后端:
java
// 秒传:MD5 已存在,直接登记文件记录,秒级完成
if (fileMapper.existsByMd5(md5)) return instantUpload(md5);
// 断点续传:返回该 MD5 已上传成功的分片号
List<Integer> parts = redis.opsForSet()
.members("upload:parts:" + md5);2. 分片上传
- 前端用
Blob.slice切 5MB 分片,并行 3~5 个上传。 - 后端只做两件事:校验权限、给每个分片签发 MinIO 预签名 URL,分片流量完全不过应用服务器。
- 分片传完后
CompleteMultipartUpload合并(对象存储侧完成)。
3. 要点与坑
- 别用 multipart/form-data 直传后端:Nginx 默认
client_max_body_size 1m会拦,改大了文件全走应用带宽,机器网卡先死。 - 分片上传完要校验:后端记录每个分片 ETag,合并前核对数量与 MD5,防止浏览器端并发冲突导致漏片。
- 上传会话(md5 → 分片状态)放 Redis,TTL 24 小时,过期清理孤儿分片(对象存储生命周期规则兜底)。
- 病毒扫描、内容审核放合并后的异步任务里做,别卡上传主链路。
表设计:文件表(id、md5、size、storage_key、bucket)+ 用户文件表(user_id、file_id、path),天然支持多人共享同一份物理文件省存储。