Skip to content

让你设计一个文件上传系统,怎么设计? ​

  • 标签:后端、系统设计、场景题

🏛 业务架构师 ​

文件上传是典型的**"业务量级决定架构"**的题,我会先问:日上传量多大?文件类型?是用户头像还是商户资质还是视频内容?

第一档:中小规模(绝大多数业务)——前端直传对象存储

后端只做两件事:签发带时效的上传凭证(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),天然支持多人共享同一份物理文件省存储。