当然可以!不通过你自己的服务器,直接从客户端(浏览器、手机App等)上传文件到OSS(对象存储)是完全可以实现的,并且是推荐的现代应用架构做法,这种方法被称为“客户端直传”。
核心原理是:由你的服务器(或云函数)生成一个临时上传凭证,客户端拿着这个凭证直接与OSS通信上传文件,这样既保证了安全性(不会泄露你账号的永久密钥),又极大地减轻了你服务器的带宽和性能压力。
下图清晰地展示了客户端直传OSS的完整流程,尤其是最安全、最常用的“应用服务器颁发STS临时凭证”方案:
sequenceDiagram participant User participant Client as 客户端(浏览器/App) participant Server as 应用服务器 participant STS as 阿里云STS服务 participant OSS as 阿里云OSS User->>Client: 1. 选择要上传的文件 Client->>Server: 2. 请求上传凭证 Server->>STS: 3. 使用主AK请求临时凭证(STS) STS-->>Server: 4. 返回临时凭证(Ak, Sk, Token) Server-->>Client: 5. 返回临时凭证及上传信息 Note over Client: 6. 使用临时凭证初始化OSS SDK Client->>OSS: 7. 直传文件(PutObject) OSS-->>Client: 8. 返回上传成功结果 Client->>Server: 9. 通知上传完成(如文件Key)
下面是几种不同的实现方式,从安全到简便排序:
方案一:使用临时访问凭证(STS)【最推荐、最安全】
这是阿里云官方推荐的安全方式,你的服务器负责生成一个有时效性(比如15分钟)的临时访问凭证(使用STS服务),这个凭证权限受限(只能上传到某个指定的目录),客户端拿到这个临时凭证后,直接用OSS的SDK完成上传。
优点:非常安全,临时凭证过期即失效,且可以精细控制权限。
缺点:仍然需要一个“服务器”端来签发临时凭证,但这个服务器的压力非常小(只需要签发凭证,不处理文件流)。
1、在阿里云RAM控制台配置:
* 创建一个RAM用户(专用于编程访问)。
* 授权这个RAM用户AliyunSTSAssumeRoleAccess
权限。
* 创建一个自定义权限策略,限制只能对your-bucket-name/upload-dir/
进行PutObject
操作。
* 创建一个RAM角色,并附加上一步创建的权限策略。
2、你的服务器端(签发凭证):
* 使用你的主AccessKey(属于上一个RAM用户)调用STS的AssumeRole
接口。
* 获取到一组临时安全凭证(AccessKeyId, AccessKeySecret, SecurityToken)。
* 将这组临时凭证、以及一个指定的上传路径(例如user_upload/{user_id}/{random_filename}.jpg
)返回给客户端。
*示例(Node.js):
// 你的服务器 API,/get-sts-token
const STS = require('ali-oss').STS;
const sts = new STS({
accessKeyId: '你的RAM用户AK',
accessKeySecret: '你的RAM用户SK'
});
app.get('/get-sts-token', async (req, res) => {
const result = await sts.assumeRole(
'acs:ram::YOUR_ACCOUNT_ID:role/your-role-name', // 上面创建的RAM角色Arn
null, // Policy,可以再这里额外细化权限,不传则使用角色自带策略
15 * 60, // Token有效期15分钟
'session-name' // 会话名
);
res.json({
accessKeyId: result.credentials.AccessKeyId,
accessKeySecret: result.credentials.AccessKeySecret,
stsToken: result.credentials.SecurityToken,
bucket: 'your-bucket-name',
region: 'oss-cn-hangzhou', // 你的Bucket区域
path:user_upload/${user_id}/${Date.now()}_${file_name}
// 建议服务器指定路径,避免客户端覆盖文件
});
});
3、客户端(上传文件):
* 从你的服务器获取到上述临时凭证和配置。
* 使用OSS JS SDK,用临时凭证初始化Client。
* 直接调用put
或multipartUpload
方法上传文件。
*示例(浏览器JS):
<input type="file" id="file" />
<script src="https://gosspublic.alicdn.com/aliyun-oss-sdk-6.20.0.min.js"></script>
<script>
const fileInput = document.getElementById('file');
fileInput.addEventListener('change', async (e) => {
const file = e.target.files[0];
// 1. 从你的应用服务器获取临时凭证
const authInfo = await fetch('/your-server-endpoint/get-sts-token').then(res => res.json());
// 2. 初始化OSS客户端
const client = new OSS({
region: authInfo.region,
accessKeyId: authInfo.accessKeyId,
accessKeySecret: authInfo.accessKeySecret,
stsToken: authInfo.stsToken,
bucket: authInfo.bucket
});
// 3. 执行上传,authInfo.path
由服务器指定,更安全
try {
const result = await client.put(authInfo.path, file);
console.log('上传成功!', result.url);
// 通知你的服务器上传完成,将 result.name (文件路径) 存入数据库
} catch (error) {
console.error('上传失败:', error);
}
});
</script>
这种方式是服务器生成一个经过签名(用你的AccessKey Secret计算)的URL,这个URL直接允许上传一个指定的文件,客户端拿到这个签名的URL后,可以直接用HTTP PUT/POST请求将文件上传到这个URL。
优点:服务器压力小,只需生成URL。
缺点:URL一旦被泄露,在有效期内可能被他人滥用上传。
1、客户端:向你的服务器申请一个上传特定文件的签名URL。
2、你的服务器:
* 生成一个唯一的文件Key(路径)。
* 使用OSS SDK生成一个用于PUT上传的签名URL,并设置有效期。
* 将签名URL和文件Key返回给客户端。
3、客户端:使用fetch
或XMLHttpRequest
,用PUT方法,将文件内容作为Body,直接请求返回的签名URL。
方案三:浏览器端签名直传【不安全,仅用于测试】
强烈不推荐在生产环境使用!
这种方式是把你的永久AccessKeyId和AccessKeySecret硬编码在客户端代码(如HTML/JS)中,客户端自己计算签名然后上传。
危险:任何人都可以通过浏览器开发者工具查看你的代码,窃取你的AccessKey,然后完全操控你的OSS和云资源,造成严重的安全事故和数据泄露。
阿里云也明确禁止这种做法。
方案 | 安全性 | 服务器压力 | 推荐度 |
STS临时凭证 | 极高(临时、受限权限) | 极小(仅签发凭证) | ★★★★★ (生产首选) |
服务端签名URL | 高(URL有效期可控) | 极小(仅生成URL) | ★★★★ (适合简单场景) |
客户端签名 | 极低(泄露永久密钥) | 无 | ★ (绝对禁止用于生产) |
最佳实践:
对于你的问题“不用服务器”,最接近的答案是方案一(STS),虽然它需要一个极轻量级的服务器(甚至可以用Serverless函数实现,例如阿里云的函数计算FC),但这个服务器不直接处理文件数据,只做身份认证和授权签发。
你可以这样理解:
传统上传 客户端 -> 你的服务器 -> OSS (服务器是中转站,压力大)
客户端直传 客户端 -> OSS (你的服务器是签证官,只发“通行证”,压力极小)
要实现安全的上传,一个只负责签发凭证的“服务器”是必要的,但它的资源消耗极低,完全可以用低成本的方式(如Serverless)实现。
文章摘自:https://idc.huochengrm.cn/fwq/14169.html
评论