如何不用服务器上传文件到OSS?

当然可以!不通过你自己的服务器,直接从客户端(浏览器、手机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。

* 直接调用putmultipartUpload 方法上传文件。

*示例(浏览器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、客户端:使用fetchXMLHttpRequest,用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

评论