前言
本文为个人项目中使用云厂商对象存储技术方案的探究
为何使用对象存储
对象存储,可以理解成专门用来存文件的云端硬盘
使用对象存储无非解决下面几个痛点
- 自建服务器存储管理成本高
- 小厂使用应用上云,云服务器存储价格贵,使用oss解析存储
- 为了更好结合云厂商的cdn,ecs服务
- 开箱即用,减少开发运维成本
使用对象存储面临的问题
对于成熟的文件存储方案简单的“把文件上传到云,把文件从云下载”就完了,还要同时解决这些问题
- 文件从哪里上传?
- 谁有权限上传?
- 上传后谁能访问?
- 后端要不要经过文件内容?
- 怎么防止用户乱传、盗传、越权传?
- 图片、头像、课程视频、私密附件分别怎么处理?
- 文件上传成功后,数据库怎么记录?
- 文件删除、审核、转码、CDN 加速怎么接?
知识点
资源访问类型
资源可以分为公共资源和私有资源
公共资源会公开给网络上所有人访问,例如一些图源站点使用oss.xxx.com/1.jpg,更换资源后缀就可以访问其他资源oss.xxx.com/1.jpg
这种方式无论是从安全性还是流量控制都不可控,所以用后端需要进行包装。例如使用xxx.com/getUrl请求后端,然后再返回图片,将资源的访问路径变成黑盒
但是如果最原始的OSS URL泄露,还是能进行访问。所以并不适合存储一些例如私密信息,付费课程源视频等
私有资源是默认任何人都不能直接访问文件,即使知道 URL 也不行。其需要配合签名URL,同时结合后端进行权限认证。例如在线课程平台只有再用户购买后才会开放权限
如果有权限,后端生成一个短时间有效的签名 URL,例如 5 分钟有效,这样用户可以在短时间内访问,过期后链接失效。即私有OSS签名下载方案
上传方案
根据上传链路不同可以分为两个方案
- 方案1:客户端 -> 业务服务器 -> OSS
- 方案2:客户端 -> OSS,业务服务器只负责发凭证/签名
当客户端不走业务服务器,就代表失去了自由控制的功能。例如我想对文件进行严格格式校验,只能让用户上传jpg格式文件,就无法做到
相反,如果链路经过业务服务器,我就能根据文件开头的二进制头特征(MIME TYPE)判断上传的文件是否属于特定格式
不同的场景用不同方案,比如用户头像上传,后端可以直接读取上传流,传输给oss不需要保存到本地。但是如果大文件需要上传于处理,会给业务服务器带来流量/带宽压力
下面是不同方案对于的不同处理流程
- 方案1:
- 用户选择文件
- 前端 multipart/form-data 上传到后端
- 后端校验登录态、文件大小、文件类型
- 后端读取文件流
- 后端上传到 OSS
- OSS 返回 object key
- 后端写入数据库
- 后端返回文件访问地址或资源 ID
- 方案2
- 用户请求业务后端:我要上传文件
- 后端校验用户身份和业务权限
- 后端生成临时上传凭证或签名
- 前端拿到凭证
- 前端直接上传到 OSS
- 上传成功后,前端通知后端
- 后端校验 object key,并写入数据库
方案1中,对于经过服务器的落盘方式,有不同的方案
- 客户端 -> 后端内存/临时文件 -> OSS
- 客户端 -> 后端磁盘 -> OSS
- 客户端 -> 后端流式转发 -> OSS(网络IO+操作系统缓冲区)
方案2中,对于客户端直传方式,有不同的方案
- 后端生成 STS 临时凭证,前端用 SDK 上传
- 后端生成 Post Policy,前端用表单直传
- 后端生成上传 Signed URL,前端 PUT 上传
客户端直传方式
1. 后端生成 STS 临时凭证,前端用 SDK 上传
STS 可以理解成:后端临时给前端发一张短期通行证。包含AccessKeyId,AccessKeySecret,SecurityToken,Expiration
后端可以限制
- 只能上传到某个 bucket
- 只能上传到 user-uploads/1001/ 目录
- 只能有效 15 分钟
- 不能删除文件
- 不能读取其他文件
流程如下:
- 前端请求 /api/oss/sts
- 后端校验用户登录
- 后端向云厂商 STS 服务申请临时凭证
- 后端返回临时凭证给前端
- 前端用 OSS SDK 直传
- 前端把 object key 通知后端
- 后端写数据库
适合:
- 大文件上传
- 图片上传
- 视频上传
- App 端上传
- Web 端上传
- 需要分片上传的场景
这是现在非常常见的方案。
2. 后端生成 Post Policy,前端用表单直传
Post Policy 可以理解成:后端提前签一个上传规则,前端拿这个规则用表单上传到 OSS。
它通常限制:
- 上传路径前缀
- 文件大小
- Content-Type
- 过期时间
流程如下:
- 前端请求 /api/oss/policy
- 后端返回 policy、signature、dir、host
- 前端构造 FormData
- 前端 POST 到 OSS
- OSS 返回成功
- 前端通知后端保存记录
优点是简单,适合图片、普通文件上传。
缺点是复杂控制能力不如 STS 灵活。
3. 后端生成上传 Signed URL,前端 PUT 上传
还有一种方式是:后端生成一个带签名的 PUT URL。
前端拿到后,PUT 文件内容到这个 URL
流程如下:
- 前端告诉后端:我要上传 avatar.png
- 后端生成 object key
- 后端生成 5 分钟有效的 PUT 签名 URL
- 前端用 fetch/axios PUT 文件到这个 URL
- 上传成功后,前端通知后端
这种方案的特点是:一个签名 URL 通常对应一个具体对象。
例如后端生成:uploads/user_1001/avatar.png。前端只能往这个对象上传。
它适合:
- 上传单个确定文件
- 权限控制简单
- 不想让前端接触 STS
- 后端希望强控制 object key
但是如果要做分片上传、大文件断点续传,通常 STS + SDK 更方便。
下载方案
公共资源的下载,一是可以让返回给前端访问固定OSS Bucket,但是没有权限控制。二是后端隐藏真实路径存储地址,下载走后端转发
私有资源不能直接访问,需要后端要生成签名URL,对应上传方案中的Signed URL,即服务器为私有文件生成带过期时间的 GET 方法预签名 URL,让客户端临时下载文件
sequenceDiagram
participant U as 用户浏览器
participant B as 业务后端
participant DB as 数据库
participant OSS as 私有 OSS Bucket
U->>B: 请求下载 fileId
B->>B: 校验登录态 / JWT / Session
B->>DB: 查询 fileId 对应的 objectKey 和权限
DB-->>B: 返回 objectKey、ownerId、资源状态
B->>B: 判断用户是否有访问权限
B->>B: 使用 AccessKey 或 STS 凭证生成 GET 签名 URL
B-->>U: 返回签名 URL
U->>OSS: 直接访问签名 URL
OSS->>OSS: 校验签名、过期时间、权限
OSS-->>U: 返回文件内容
当客户端访问URL时,OSS会校验,随后触发下载
对于这种方式,服务器数据库需要记录下面字段
file_id: 10001
owner_id: 20001
bucket: itifei-01
object_key: user-upload/20001/avatar/a.png
visibility: private
真正下载时,后端根据 object_key 临时生成签名 URL
sequenceDiagram
participant U as 用户
participant API as 后端 API
participant Auth as 鉴权模块
participant DB as 数据库
participant Signer as OSS SDK 签名器
participant OSS as OSS
U->>API: GET /api/files/10001/share-url
API->>Auth: 校验用户身份
Auth-->>API: userId = 20001
API->>DB: 查询 fileId = 10001
DB-->>API: objectKey = user-upload/20001/a.png
API->>API: 判断 userId 是否有权限
API->>Signer: 生成 GET 预签名 URL,有效期 1 小时
Signer-->>API: signedUrl
API-->>U: 返回 signedUrl
U->>OSS: GET signedUrl
OSS-->>U: 文件内容
流程如下:
- 用户请求:我要看课程视频
- 后端校验用户是否购买课程
- 后端查询视频 object key
- 后端生成短期有效的下载 URL
- 前端拿 URL 播放或下载

评论(0)
暂无评论