私有文件怎么既安全,又不堵住服务器
私有图片、PDF 和成果附件,该由谁管权限,又该由谁搬运文件?

用户上传的成果图、行动证据和学习资料,都属于私有内容。随着这类内容增加,存储方案要同时满足两件事。
文件只能由有权限的人访问。
文件传输不能持续经过业务服务。
只做前一件事,容易把 OSS 桶或文件地址暴露出来。只做后一件事,App 每上传或查看一个文件,都要经过 Server 中转。用户内容增长后,图片和 PDF 的字节流会占满 Server 的带宽和连接数,业务接口反而成了流量瓶颈。
我们的方案很直接。
Server 负责判断谁有权访问。App 直接和 OSS 上传、下载文件。
这不是把权限交给客户端。相反,权限仍然由 Server 控制,只是文件字节不再由 Server 搬运。
Server 管授权,OSS 管文件传输
私有文件链路分成控制面和数据面。
控制面由 Server 处理。App 发起访问请求时,Server 校验登录身份、账号归属、业务关系和对象状态。通过校验后,Server 才为一个确定的 OSS 对象签发短期访问能力。
数据面由 App 和 OSS 直接处理。App 拿到短期能力后,直接上传或下载对象。图片、PDF 和其他大文件不经过 Server。

图里的细箭头是控制面,负责身份校验和授权。粗箭头是数据面,负责实际文件传输。
Server 不再为每次图片展示承担带宽压力,仍然保留完整的业务权限判断。OSS 专门处理高并发的对象上传和下载,后续也可以自然接入私有 CDN。
一次上传和查看如何完成
下图把两个阶段放在一起。上传阶段,Server 只创建上传意图、校验业务关系并签发 PUT 能力。文件从 App 直接写入 OSS。完成阶段,Server 再向 OSS 核验真实对象,确认后才写入业务关联。
查看阶段同样如此。Server 根据资源和成果关系完成授权,返回短效 GET 能力。App 直接从 OSS 读取图片字节。

这张图里最重要的边界没有变化。Server 参与每一次权限决定,但不参与图片字节的传输。
上传时,先授权,再确认归属
上传不是让 App 随意选择一个 OSS 路径,再把结果告诉 Server。对象位置必须由 Server 根据业务场景和当前账号分配。
流程分为三步。
- App 申请上传资格,Server 校验文件类型、大小、配额和业务场景,并创建待上传对象。
- App 使用短期上传能力,直接把文件传到私有 OSS。
- App 通知 Server 上传完成,Server 向 OSS 核验对象的真实元数据,确认后再将对象绑定到成果、行动证据或资料。
第三步不能省。它让业务数据和对象存储状态保持一致,也方便处理上传中断、重复提交和无人认领的临时文件。
下载时,短期 URL 只是一把临时钥匙
查看图片或下载资料时,App 先请求某个业务对象。Server 从业务记录中找到该对象,确认当前用户有权限后,生成一个短期下载 URL。App 再直接向 OSS 获取文件。
这个 URL 只允许访问一个对象,并且有很短的有效期。它不是公开地址,也不是应该长期保存的数据。拿到 URL 的人在有效期内可以访问对象,因此 URL 不应写入数据库、日志、分享链接或长期磁盘缓存。
业务系统保存稳定的对象 ID、归属账号、文件类型、大小和状态。每次访问都重新授权、重新生成短期 URL。
App 对私有图片的缓存也要按这个边界设计。URL 会失效,不适合作为长期缓存键。更适合的做法是按账号和私有对象 ID 缓存已下载的图片字节,并在登出或切换账号时清理。这样既能避免详情页重复加载造成的闪烁,也不会把其他账号的私有内容遗留在缓存里。
权限边界必须收在 Server
这套模式能成立,依赖几个明确的边界。
- OSS 桶保持私有,不允许匿名读取或写入。
- App 不能提交任意 bucket 或对象路径来申请签名。
- Server 只能为数据库中已登记、且与当前业务关系匹配的对象生成短期能力。
- 上传能力和下载能力分别限制到对应的方法、对象和有效期。
- 资料转换等后台任务也通过受控的短期能力直接从 OSS 获取文件,不把大文件先经由业务接口转发。
这样做的重点不是隐藏文件路径。路径再复杂也不是权限控制。真正的权限控制发生在 Server 签发短期能力之前。
这套方案增加了哪些工作
相比把文件 URL 直接写进业务表,这套方案需要维护对象状态、上传确认和临时文件清理。还需要正确配置 App 直连 OSS 所需的跨域规则和请求限制。
App 还需要有专门的私有图片组件,不能直接套用普通网络图片组件。普通图片通常把完整 URL 当作缓存键,私有图片的 URL 会过期,每次签发也可能不同。
私有图片组件只接收私有对象 ID 和展示参数。它先按账号和对象 ID 查找会话内缓存。缓存命中时直接绘制,避免从甲程详情进入成果详情时重新下载造成闪烁。缓存未命中时,组件向 Server 申请短效 GET URL,再直接从 OSS 下载图片字节。
下载成功后,组件只缓存图片字节,不缓存 URL。登出或切换账号时清理这块缓存。短效 URL 过期或请求失败时,组件重新申请 URL 后有限重试。这样,缓存策略、过期处理和账号隔离都集中在一个标准组件里,而不是分散在每个页面。
这些工作换来了更清晰的长期边界。Server 的职责是鉴权、审计和业务状态。OSS 的职责是私有对象存储和高流量传输。App 的职责是直接上传、直接下载,以及通过私有图片组件完成会话内展示缓存。
面对即将增长的用户侧私有内容,这个分工能让系统在规模变化时保持稳定。增加用户和文件数量时,主要扩展的是 OSS 的数据面能力,而不是让 Server 变成所有文件的必经通道。
方案的限制和未来问题
这个方案把大文件流量从 Server 移走了,但没有让复杂度消失。下面这些问题需要在设计和运营阶段持续处理。
| 问题 | 影响 | 应对方向 |
|---|---|---|
| 预签名 URL 是临时凭证 | URL 泄露后,任何拿到它的人都可以在有效期内访问对象 | 有效期保持短,不写入日志或数据库,响应使用 no-store,避免第三方跳转和分享 |
| 权限变更不能即时收回已签发 URL | 用户被移出甲程后,旧 URL 在到期前仍可能有效 | 缩短有效期,敏感操作重新签发,必要时结合对象版本或网关鉴权 |
| Server 仍承担授权请求 | 浏览大量缩略图时,签名接口可能成为新的小流量热点 | 合并列表中的签名请求,做速率限制和观测,不让任意对象路径进入签名接口 |
| App 直连依赖网络和跨域配置 | 请求头、时钟、网络切换或 CORS 配置错误都会导致上传下载失败 | 固化私有对象组件和上传客户端,提供 URL 过期刷新、有限重试和可观测错误码 |
| 上传与业务提交是两个阶段 | 可能留下未绑定对象,或出现重复确认 | 使用上传状态机、幂等确认和过期清理任务 |
| MD5 只能证明传输一致 | 它不能证明文件安全,也不能识别伪装内容 | 服务端核验大小和类型,必要时增加图片解码、恶意文件扫描和隔离状态 |
| OSS 不再经过 Server,不等于没有成本 | 带宽、请求次数、原图尺寸和重复下载仍会带来费用 | 生成缩略图,限制原图尺寸,按访问量引入私有 CDN 和图片处理 |
未来资料类型会继续扩大。大 PDF、音视频、分片上传、断点续传和范围读取都会让上传客户端和对象状态管理更复杂。它们仍应复用同一条边界,Server 校验业务权限并签发受限能力,客户端或受控 Worker 与 OSS 直接交换文件。
当需要更严格的撤销能力、更长时间的在线播放或更高的外网访问量时,短效预签名 URL 可能不再是唯一入口。可以在 OSS 前增加私有 CDN、边缘鉴权或专门的下载网关。但业务权限判断与数据传输分离的原则仍然成立。
结论
私有文件需要安全控制,也需要高效传输。这两项要求不冲突。
Server 负责判断谁能访问,并为具体对象发放短期钥匙。App 直接使用钥匙与 OSS 上传和下载。OSS 承担文件流量,Server 不承担文件中转。
这让私有内容既能保持权限可控,也能在用户规模上升时保持可扩展。