ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

设计在线文件分享系统:从分片上传到对象存储的核心决策

设计在线文件分享系统:从分片上传到对象存储的核心决策 Design an Online File-sharing System | Preparation很多人拿到设计一个在线文件分享系统这道题第一反应是画一张架构图前端、后端、对象存储、CDN再加上一个消息队列看起来五脏俱全。但到了追问环节问题就出来了——分片大小为什么选 4MB 而不是 16MB元数据表的主键怎么设计分享链接过期了资源要不要物理删除断点续传是客户端假象还是服务端真的记住了每个字节这些细节一旦答不上来前面的架构图反而成了减分项。这篇文章不打算再给你一张万能架构图。既然标题是 Preparation我们就站在开始动手设计之前把这道题真正需要想清楚的东西拆解一遍需求边界、核心概念、架构层次、数据模型、API 约定、存储选型、安全边界以及面试官最可能追着问的十个细节。你能带着一套可以落地的设计方案离开这篇文章而不是只记住几个名词。1. 这篇文章真正要解决的问题文件分享系统看起来入门门槛很低用 Spring Boot 加 MinIO 两个晚上就能跑通一个 Demo。但生产级的在线文件分享系统难点集中在四层传输层文件比普通接口数据大几个数量级怎么传得又快又不阻塞应用服务器。元数据层文件本体可以丢给对象存储但文件属于谁、能分享给谁、过期时间是什么时候这些必须自己管清楚。一致性层分片上传中途断了重传时怎么对接已有分片多个客户端同时写同一个文件怎么处理冲突。安全层文件分享系统天然是泄露风险高发地未授权访问、链接盗用、恶意文件上传每个场景都要提前兜底。所以如果你正要准备系统设计面试或者在规划一个真实的文件分享产品真正需要的是下面这些问题的答案怎么把能上传文件升级成能稳定地上传大文件。怎么设计一份既不过度也不缺失的数据库表结构。怎么用 API 把这些能力暴露给前端或第三方客户端。怎么在成本和体验之间做出有理有据的取舍。本文会逐一拆解。读完之后你可以做到的验收标准是在纸上画得出模块边界能写出核心表结构和关键接口文档并且能回答面试官关于秒传、断点续传、分片大小、权限模型这四个追问。2. 基础概念与核心原理在进入架构设计之前先把文件分享系统里几个容易混淆的概念理清楚。这些术语在准备阶段不弄明白后面设计一定会跑偏。2.1 对象存储文件分享系统最常用的存储底座是对象存储代表产品有阿里云 OSS、腾讯云 COS、AWS S3以及开源界的 MinIO、Ceph RADOS。对象存储不是传统意义上的文件系统它没有目录树存储单元是一个个带唯一键的对象。你上传一个avatar.jpg它存进去的是键为avatar.jpg或你指定的 key、值为文件内容的对象。为什么文件分享系统适合对象存储而不是自建 NAS关键在三点对象存储天然支持 HTTP 接口和 Web 应用集成成本低。对象存储的扩容是水平式的单个桶容量可以做到 PB 级不需要自己运维磁盘阵列。对象存储自带跨区域冗余和多版本能力数据可靠性远高于单机磁盘阵列。2.2 分片上传与断点续传大文件上传不能走一次 HTTP 请求把整个文件塞进去原因在于网络是脆弱的连接随时可能中断一旦中断整个文件都要重传。分片上传的思路是把文件切成 N 个固定大小的分片逐个上传。服务端记录哪些分片已经到了客户端下次只补传缺失的分片这就是断点续传的底层逻辑。这里有一个很容易答错的细节断点续传的断点记录在哪一端正确的认知是两层都有。客户端记录本地已完成的分片列表服务端在元数据表里也记录已接收的分片序号。两端对比之后客户端只需要重传服务端没有的部分。如果只靠客户端记录换一台设备或者清缓存续传就失效了。2.3 秒传与文件去重秒传不是真的传输速度上秒级而是服务端发现目标文件已经存在、内容完全一致于是直接复用已有对象不再接收文件体。秒传的依据是文件指纹通常是 MD5 或 SHA-1 加文件大小。凡是宣称秒传的功能实现路径都绕不开两步客户端先上传文件摘要大小 哈希值。服务端查元数据库命中直接返回上传成功。2.4 直传与转传上传链路有两种典型设计。转传是前端把文件先交给你的应用服务器再由应用服务器写入对象存储直传是前端拿到一个临时上传凭证绕过应用服务器直接上传到对象存储。转传的优点是应用服务器可以在上传过程中做校验、鉴权、内容扫描缺点是大文件会占用应用服务器的带宽和内存而且多了一次网络中转速度更慢。直传的优点是快、省服务器资源缺点是校验能力弱必须在客户端先完成内容校验和数据完整性计算。实际生产系统中主流的方案是直传为主、转传为辅大文件走直传由服务端签发带过期时间的临时凭证小文件或需要服务端即时处理的场景走转传。下面的表格总结了四组核心概念在文件分享系统里的定位概念解决什么问题容易混淆的点对象存储文件本体的海量存储与可靠冗余不等同于传统文件系统不建议存大量小文件的目录结构分片上传大文件传输的稳定性与速度分片不只客户端的事服务端要配合记录状态断点续传传输中断后不重传整个文件续传状态需要服务端持久化不能只依赖客户端秒传相同内容不重复存储不等于传输速度极快而是服务端直接去重3. 需求分析先定义清楚边界设计任何系统第一步不是画架构图而是把需求边界定清楚。文件分享系统的需求可以从三个维度分析。3.1 功能需求一个最小可用的在线文件分享系统至少包含以下功能用户注册与登录。文件上传与下载。文件列表与元数据展示名称、大小、上传时间。文件删除、重命名。生成分享链接支持设置有效期。通过分享链接在免登录状态下下载文件。文件预览图片、纯文本等浏览器可直接渲染的类型。这里建议在准备阶段再加两个进阶功能因为它们能显著区分设计深度分片上传与断点续传。同一文件的多人协作上传同名文件冲突处理。3.2 非功能需求非功能需求决定架构选型。你需要提前对几个指标做出合理的假设而不是面试时现场拍脑袋用户规模先假设注册用户 1000 万日活跃用户 100 万。存储量单文件上限 2GB平均文件大小 20MB人均存储量 5GB。并发量峰值每秒上传请求 2000下载请求 5000。可用性目标 99.9%允许每月约 43 分钟不可用。一致性文件内容强一致元数据最终一致。上传成功之前不可见下载时不会读到写入一半的文件。3.3 明确不做什么好的设计者会主动划掉不做的需求。文件分享系统在准备阶段通常建议明确排除在线文档编辑需要锁机制和协同算法复杂度完全不同。文件版本历史保留最近 N 份会增加存储设计复杂度MVP 阶段可以不做。跨地域自动同步和冲突合并类似 Dropbox 的多端同步是更高阶的题目。明确不做什么一方面能避免设计发散另一方面也向面试官或评审人展示了需求收敛意识。4. 系统整体架构与核心流程在需求边界清楚之后架构就水到渠成了。文件分享系统的整体架构可以按依赖方向拆成六个模块。4.1 模块划分接入层API Gateway / Nginx负责统一入口、限流、TLS 终止把请求路由到不同服务。应用服务层用户服务注册、登录、Token 签发。文件元数据服务文件记录、目录层级、重命名。上传服务预签名生成、分片接收状态管理。分享服务分享链接的创建、失效、访问计数。存储层对象存储集群。元数据库存储文件记录、用户、分享链接的数据库生产环境优先选择高可用的 MySQL 或 PostgreSQL 集群。缓存层Redis 用于会话缓存、热点分享链接缓存、上传空闲分片的临时状态。异步任务层消息队列解耦文件上传完成之后的处理和文件删除之后的对象清理。这里有一个被很多刚接触系统设计的人忽略的点应用服务不直接读写文件内容只操作元数据。上传文件在业务语义上其实是两步——先存对象再记录元数据。把这两步解耦系统的扩展性会好很多。4.2 上传流程一次完整的分片上传流程可以拆成四个阶段初始化上传客户端调用POST /files/uploads携带文件名、文件大小、分片大小、文件摘要。服务端生成uploadId和完整的分片序号集合写入元数据表状态为init。预签名获取客户端请求获取某个分片的上传地址。服务端签发对象存储的预签名 URL有效期通常 10 到 30 分钟。分片直传客户端直接 PUT 文件分片到预签名 URL。服务端通过对象存储的事件通知或回调接口更新分片接收状态。合并完成所有分片就绪后客户端调用POST /files/uploads/{uploadId}/complete。服务端合并分片对象校验最终文件摘要更新文件元数据为available。4.3 下载流程下载流程比上传简单典型的读取路径是客户端请求GET /files/{fileId}/download或访问一个分享链接。应用服务校验权限确认用户对该文件有读权限或分享链接有效。服务端生成对象存储的下载预签名 URL有效期可以设短比如 10 分钟。客户端 302 跳转到该 URL对象存储直接把文件流返回给客户端。CDN 节点缓存热点文件降低对象存储的出流量。下载流程中真正值得深入准备的点是分享链接的鉴权要放在应用服务层做不能把永久对象存储 URL 直接暴露给客户端。一旦永久 URL 泄漏,文件的访问控制就全部失效了。5. 数据模型设计文件分享系统的数据模型是整个设计中最能体现落地感的部分。下面给出一版可以直接作为设计稿的表结构并逐一解释设计原因。5.1 核心表结构数据库选用 MySQL 8.x字符集utf8mb4引擎 InnoDB。核心表共四张用户表、文件元数据表、分享链接表、分片状态表。-- 文件路径sql/schema.sql CREATE TABLE users ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, username VARCHAR(64) NOT NULL, password_hash VARCHAR(128) NOT NULL, email VARCHAR(128) DEFAULT NULL, max_storage_bytes BIGINT NOT NULL DEFAULT 5368709120, -- 默认 5GB created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE files ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, owner_id BIGINT UNSIGNED NOT NULL, file_name VARCHAR(255) NOT NULL, file_size BIGINT NOT NULL, storage_key VARCHAR(512) NOT NULL, -- 对象存储在桶中的唯一 key content_md5 CHAR(32) NOT NULL, -- 文件摘要用于秒传与完整性校验 upload_id VARCHAR(64) DEFAULT NULL, -- 分片上传会话 ID status TINYINT NOT NULL DEFAULT 0, -- 0init 1uploading 2available 3deleted category VARCHAR(16) DEFAULT file, -- file | image | video | text便于前端预览 created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, KEY idx_owner_id_status (owner_id, status), KEY idx_upload_id (upload_id), KEY idx_content_md5 (content_md5, file_size) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE share_links ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, file_id BIGINT UNSIGNED NOT NULL, share_token CHAR(32) NOT NULL, -- 对外分享的短 token不暴露数据库自增 ID created_by BIGINT UNSIGNED NOT NULL, expires_at DATETIME DEFAULT NULL, -- NULL 表示永久但业务上建议设上限 max_downloads INT DEFAULT NULL, -- NULL 表示不限次数 download_count INT NOT NULL DEFAULT 0, status TINYINT NOT NULL DEFAULT 1, -- 1active 0cancelled created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_share_token (share_token), KEY idx_file_id (file_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE upload_parts ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, upload_id VARCHAR(64) NOT NULL, part_number INT NOT NULL, -- 分片序号从 1 开始 part_size BIGINT NOT NULL, storage_key VARCHAR(512) NOT NULL, -- 每个分片在对象存储里独立存储 status TINYINT NOT NULL DEFAULT 0, -- 0pending 1received 2confirmed received_at DATETIME DEFAULT NULL, UNIQUE KEY uk_upload_id_part (upload_id, part_number) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;5.2 表设计的关键决策存储密钥隔离files.storage_key不能暴露用户的真实文件名否则会带来两个问题一是文件名特殊字符可能引发对象存储的路径解析问题二是真实文件名加上对象存储的预签名 URL 一旦泄漏文件可以直接被下载。推荐的做法是storage_key使用{uid}/{uuid}/{hash}这种不包含业务信息的随机结构。文件摘要索引content_md5和file_size的组合索引是秒传的核心。上传初始化时服务端先按摘要查这张表如果已有同样大小和哈希的文件处于available状态直接复用storage_key不需要再接收文件体。分片表不做物理外键这里的upload_id是逻辑关联不建立外键约束。原因是分片状态更新非常高频外键约束会在每次更新时产生额外的一致性检查开销在高写入场景下不值得。完整性由应用层保证。分享链接使用 share_token 而不是自增 ID自增 ID 可被猜测爬虫可以从share_link?id1一路遍历到id1000000。32 位随机 token 的防猜测能力好得多。5.3 同一文件去重的并发处理文件名相同的上传并不是需要考虑的设计重点。需要提前设计的是同一个 uploadId 的并发分片提交。客户端可能同时并发提交多个分片服务端更新分片状态时必须保证幂等-- 分片接收确认的幂等更新 UPDATE upload_parts SET status 1, received_at NOW() WHERE upload_id ? AND part_number ? AND status 0;如果UPDATE影响行数为 0说明该分片之前已经被接收过客户端可以直接确认成功。这个写法避免了并发场景下分片状态被重复覆盖的问题。6. API 设计与接口约定数据模型定下来之后API 设计就有了依据。下面用 OpenAPI 风格给出核心接口的定义。# 文件路径docs/openapi.yaml openapi: 3.0.0 info: title: Online File Sharing API version: 1.0.0 paths: /files/uploads: post: summary: 初始化分片上传 requestBody: required: true content: application/json: schema: type: object required: [fileName, fileSize, partSize, contentMd5] properties: fileName: type: string fileSize: type: integer partSize: type: integer default: 4194304 contentMd5: type: string responses: 200: description: 初始化成功返回 uploadId 和分片列表 content: application/json: schema: type: object properties: uploadId: type: string parts: type: array items: type: object properties: partNumber: type: integer uploadUrl: type: string /files/uploads/{uploadId}/complete: post: summary: 合并分片完成上传 parameters: - name: uploadId in: path required: true schema: type: string requestBody: content: application/json: schema: type: object properties: parts: type: array items: type: object properties: partNumber: type: integer etag: type: string responses: 200: description: 合并完成返回文件元数据 content: application/json: schema: type: object properties: fileId: type: integer fileName: type: string fileSize: type: integer status: type: string /files/{fileId}/share: post: summary: 创建分享链接 parameters: - name: fileId in: path required: true schema: type: integer requestBody: content: application/json: schema: type: object required: [expiresAt] properties: expiresAt: type: string format: date-time maxDownloads: type: integer responses: 200: description: 分享链接创建成功 content: application/json: schema: type: object properties: shareToken: type: string shareUrl: type: string /s/{shareToken}: get: summary: 通过分享 token 下载文件免登录 parameters: - name: shareToken in: path required: true schema: type: string responses: 302: description: 重定向到对象存储预签名 URL关于上传 API 有一个常见的面试讨论点为什么不用PUT /files/{id}直接传文件体原因在于文件分享系统需要支持大文件、断点续传和秒传。如果一次 HTTP 请求直接完成整个文件上传客户端上传一半断线服务端既不知道也不能复用已传的部分。分片上传流程中对象存储的预签名 URL 承担了最终的文件体写入应用层 API 只负责状态编排这既保证了扩展性也让应用服务器不被大流量拖垮。7. 关键设计决策存储选型与分片策略7.1 存储选型对比方案优点缺点适用场景单机文件系统本地磁盘部署简单、读延迟低容量受限、无冗余、扩容难个人项目、教学 Demo自建分布式文件系统Ceph可控性强、无供应商锁定运维成本极高、需要专业团队大型自建基础设施云对象存储OSS/COS/S3免运维、容量弹性、自带冗余有出流量费用、跨云迁移成本高绝大多数互联网产品MinIO 自建对象存储兼容 S3 API、私有化部署需要自己维护集群和监控告警私有化部署、数据要留在本地从整体成本与运维复杂度来看线上文件分享系统优先选择云对象存储。面试时可以这样表达文件本体我将采用对象存储作为持久化底座因为它在容量、可靠性、成本三者之间取得了对大多数团队都合理的平衡如果我们讨论的是私有化场景我会切换到 MinIO 或 Ceph。这种表达同时展示了产品思维和技术落地的双重考虑。7.2 分片大小怎么定分片大小没有绝对的正确答案但在不同的约束下有不同的合理区间4MB 到 8MB最常见的选择。既能控制单分片的内存占用又能控制分片数量。2GB 文件按 4MB 分片为 512 片元数据表的记录数约 512 行完全可接受。低于 1MB单分片传输快但分片数量爆炸HTTP 请求数过多反而增加总耗时。高于 50MB分片数量少但单分片失败重试的代价大且对象存储的直传 URL 通常有大小限制。更合理的表达是分片大小应该与期望的最大文件大小联动。如果产品规定单文件最大 2GB、单请求最大 5MB那么按 4MB 分片是合理的如果单文件最大只有 100MB分片设在 5MB 到 10MB 可能更好。7.3 秒传的实现细节秒传的本质是服务端在接到上传初始化请求时先按content_md5 file_size去查文件表。命中可用记录后直接把两条记录关联起来。实现上有两个容易被忽略的细节。第一只比较哈希是不够的必须同时比较文件大小。两个不同文件极少出现同大小同哈希但如果只比较哈希不比较大小攻击者可以用一个精心构造的冲突文件占用别人的秒传记录。第二秒传只能作用于文件内容完全相同的场景服务端不需要读取已存储对象的内容再比较。对象存储已经存了这个文件content_md5是在首次上传时计算并持久化的后续秒传只是信任历史记录不需要重新计算。8. 安全与权限设计文件分享系统的安全设计可以说决定了整个系统的下限。我按四个方面展开。8.1 认证与授权用户侧认证使用 JWT短期 Token加 Refresh Token 双机制。JWT 的有效期建议不超过 2 小时防止 Token 泄露后的长时间滥用。每种资源的访问权限必须在应用服务层校验用户只能操作属于自己的文件owner_id必须等于当前登录用户 ID。分享链接访问不需要登录但必须校验share_links表中的status、expires_at、max_downloads字段。文件下载必须走向量存储预签名 URL不能直接暴露永久 URL。8.2 上传内容安全上传接口天然暴露给不受信任的客户端必须做以下检查文件类型校验不能只信扩展名要读文件头magic number判断真实类型。恶意文件扫描上传完成后的异步任务调用病毒扫描或内容安全服务发现风险立即标记并隔离。大小限制客户端声明的大小和服务端实际接收到的分片大小必须逐片核对防止上传超出配额。8.3 下载链接防盗预签名 URL 的过期时间不宜太长。下载预签名 URL 建议设定 10 分钟有效期上传预签名 URL 可以放宽到 30 分钟因为分片上传的完整会话已经由uploadId管控。分享链接的 token 要保证足够的随机熵32 位十六进制随机字符串128 位熵是合理选择。8.4 隐私与加密文件内容在对象存储中默认可以使用服务端加密SSE。需要强调的是加密并不能替代权限控制——加密保护的是存储介质层面的泄露而预签名 URL、分享链接鉴权保护的才是访问者身份的合法性。两个安全层缺一不可。9. 性能优化与可扩展性文件分享系统是典型的读多写少、流量不均负载模型性能优化可以按下面几个层次展开。9.1 热点文件与 CDN下载场景非常符合 CDN 的使用范式内容静态、读多写少、地域分散。上传完成后服务端可以把文件的storage_key主动预热到 CDN 节点并将下载 URL 指向 CDN。CDN 回源只需要回源到对象存储源站带宽压力被大幅降低。9.2 元数据缓存策略文件列表查询和文件元数据查询是高频读操作。建议以fileId → 文件元数据的形式在 Redis 中缓存过期时间可以结合业务场景设定比如 10 分钟。当文件被重命名或删除时主动删除对应缓存键。禁止使用全文件列表缓存因为大列表的缓存失效逻辑复杂容易产生数据不一致。9.3 大文件传输的限流与配额控制上传接口必须有配额限制。配额控制的本质不是限制用户而是保护系统。每位用户单独控制上传并发数比如同时最多 3 个 Upload 会话全局限流可以按每秒请求数做超出后直接拒绝并返回重试提示。9.4 异步化处理文件上传完成后的善后工作——生成缩略图、内容审核、病毒扫描、更新用户存储用量统计——全部丢进消息队列异步执行。应用服务在complete接口里要做的只是确认分片合并完成并落一条元数据其他耗时操作全部异步用户不需要等。关于这一层有一个实践中的常见反例有些团队会在complete接口里同步做缩略图生成和审核回调导致一次上传耗时超过 5 秒。异步化之后接口响应可以控制在 300ms 以内用户体感完全不同。10. 常见问题与设计盲区以下是文件分享系统在准备阶段最常见的问题与排查思路建议收藏起来当检查清单用。问题可能原因设计对策上传大文件超时应用服务器默认超时时间过短或走了转传链路采用分片直传调大网关的超时配置业务请求与文件流分离断点续传失效服务端没有持久化分片状态只依赖客户端本地记录upload_parts表记录每个分片状态客户端按服务端返回的缺失列表补传秒传没生效内容摘要字段没建立索引或数据库里没有复合索引创建(content_md5, file_size)复合索引秒传查询走索引分享链接被遍历使用自增 ID 作为分享标识改用 32 位随机 share_token文件删除后下载仍成功只删了元数据没有删对象存储中的实际文件删除文件采用标记删除 异步清理对象两步分享链接同步失效CDN 缓存了已删除文件没有主动刷新 CDN 缓存删除文件后调用 CDN 刷新接口或者签名 URL 使用 Bucket 独立鉴权同时上传同一文件产生多份副本秒传逻辑没有先查重初始化上传前先执行content_md5 file_size精确查重11. 最佳实践与工程建议11.1 分环境配置文件分享系统的配置建议从第一天就拆成开发、测试、预发、生产四套环境。对象存储的 Bucket 名称、预签名 URL 的过期时间、分片大小上限、单用户并发数、CDN 开关都应该放在配置中心不要硬编码到代码里。下面是一个配置示例# 文件路径src/main/resources/application-production.yml file-share: storage: provider: aliyun-oss bucket: prod-file-share region: oss-cn-hangzhou upload: default-part-size: 4194304 max-part-size: 10485760 max-concurrent-parts: 4 presigned-url-expire-seconds: 1800 download: presigned-url-expire-seconds: 600 enable-cdn: true security: share-token-bytes: 32 share-link-max-expire-days: 30 quota: default-max-storage-bytes: 536870912011.2 命名与目录规范对象存储的storage_key要约定清晰。推荐格式uploads/{userId}/{yyyyMMdd}/{randomHex}/{hash}。这样做的目的是让对象存储的列表操作能按用户和日期做粗粒度的过滤虽然生产系统不应该频繁 List Bucket但必要时的排查效率会高很多。11.3 日志与监控文件上传下载全链路必须有追踪日志至少记录四个指标上传初始化到完成的耗时和成功率。分片接收成功率与重试率。对象存储的预签名 URL 签发数和带宽峰值。CDN 命中率和回源带宽。如果要把文件分享系统做成一个真正的线上产品监控不能只看应用服务器指标必须看对象存储的独立出流量和 CDN 回源费用。文件分享系统的最大成本往往不是服务器而是存储量与出流量。11.4 数据一致性与回滚文件元数据更新涉及重命名、删除、移动等操作时推荐采用乐观锁。files表中增加version字段每次更新都带版本号版本号不匹配则更新失败。这样可以避免两个客户端同时重命名同一个文件导致的最后写入覆盖问题。-- 乐观锁更新示例 UPDATE files SET file_name ?, version version 1 WHERE id ? AND owner_id ? AND version ?;11.5 生产环境的权限与风险控制如果文件分享系统需要对接企业内部存储或生产数据必须遵守最小权限原则应用服务使用的对象存储账号只授权目标 Bucket 的PutObject、GetObject、DeleteObject不给 List Bucket 和全局管理权限。删除操作在生产环境必须走软删除 延迟清理策略先标记statusdeleted保留 N 天后由定时任务清理。12. 总结与准备清单回到最开始的问题设计一个在线文件分享系统准备阶段最应该带走的是什么第一架构不是一张图而是一组决策。对象存储选型、分片大小、秒传策略、上传链路、权限模型每一个决策背后都有成本和收益的权衡。能说清楚为什么这么选比画出一张漂亮的图更重要。第二数据模型是设计的锚点。files表和upload_parts表的结构直接决定了分片上传、断点续传、秒传和分享链接能不能顺畅落地。准备阶段花大量时间在表结构上是完全值得的。第三安全不能是后置需求。分享 token、预签名 URL、文件类型校验、访问控制这些都是文件分享系统的地基必须第一时间进入设计稿。建议你下一步做三件事在本地用 Spring Boot 加 MinIO 把分片上传和断点续传跑通用文中的 SQL 建一套表结构模拟上传初始化、分片确认、合并完成三个主要流程然后找一个朋友配合实际用分享链接下载一次文件观察预签名 URL 的过期行为。跑通这三个环节之后这道系统设计题对你来说就不再是准备过而已而是真正掌握。
返回列表