详情页标题前

阿里云对象存储OSS客户端直传概述-云淘科技

详情页1

客户端直传是指客户端直接上传文件到对象存储OSS。相对于服务端代理上传,客户端直传避免了业务服务器中转文件,提高了上传速度,节省了服务器资源。本文介绍客户端直传的方案优势、安全实现和实践参考。

为什么客户端直传

在典型的服务端和客户端架构下,常见的文件上传方式是服务端代理上传:客户端将文件上传到业务服务器,然后业务服务器将文件上传到OSS。在这个过程中,一份数据需要在网络上传输两次,会造成网络资源的浪费、增大服务端的资源开销。为了解决这一问题,您可以在客户端直连OSS来完成文件上传,无需经过业务服务器中转。

阿里云对象存储OSS客户端直传概述-云淘科技

如何实现客户端直传

实现客户端直传需要解决以下两大问题:

跨域访问

如果您的客户端是Web端或小程序,您需要解决跨域访问被限制的问题。浏览器以及小程序容器出于安全考虑,通常都会限制跨域访问,这一限制也会限制您的客户端代码直连OSS。您可以通过配置OSS Bucket的跨域访问规则,来允许您的Web端或小程序的域名直接访问OSS。更多信息,请参见跨域设置。

安全授权

上传文件到OSS需要使用RAM用户的访问密钥(AccessKey)来完成签名认证,但是在客户端中使用长期有效的访问密钥,可能会导致访问密钥泄露,进而引起安全问题。为了解决这一问题,您可以选择以下方案实现安全上传:

  • 服务端生成STS临时访问凭证

    对于大部分上传文件的场景,建议您在服务端使用STS SDK获取STS临时访问凭证,然后在客户端使用STS临时凭证和OSS SDK直接上传文件。客户端能重复使用服务端生成的STS临时访问凭证生成签名,因此适用于基于分片上传大文件、基于分片断点续传的场景。需要注意的是,频繁地调用STS服务会引起限流,因此建议您对STS临时凭证做缓存处理,并在有效期前刷新。更多信息,请参见什么是STS。

  • 服务端生成Post签名和Post Policy

    对于要限制上传文件的属性的场景,您可以在服务端生成PostObject所需的Post签名、PostPolicy等信息,然后客户端可以凭借这些信息,在一定的限制下不依赖OSS SDK直接上传文件。您可以借助服务端生成的PostPolicy限制客户端上传的文件,例如限制文件大小、文件类型。此方案适用于通过HTML表单上传的方式上传文件。需要注意的是,此方案不支持基于分片上传大文件、基于分片断点续传的场景。更多信息,请参见PostObject。

  • 服务端生成签名URL

    对于简单上传文件的场景,您可以在服务端使用OSS SDK生成PutObject所需的签名URL,客户端可以凭借签名URL,不依赖OSS SDK直接上传文件。需要注意的是,此方案不适用于基于分片上传大文件、基于分片断点续传的场景。在服务端对每个分片生成签名URL,并将签名URL返回给客户端,会增加与服务端的交互次数和网络请求的复杂性。另外,客户端可能会修改分片的内容或顺序,导致最终合并的文件不正确。更多信息,请参见在URL中包含签名。

服务端生成STS临时访问凭证

服务端通过STS临时访问凭证授权客户端上传文件到OSS的过程如下。

阿里云对象存储OSS客户端直传概述-云淘科技

  1. 客户端向业务服务器请求临时访问凭证。

  2. 业务服务器使用STS SDK调用AssumeRole接口,获取临时访问凭证。

  3. STS生成并返回临时访问凭证给业务服务器。

  4. 业务服务器返回临时访问凭证给客户端。

  5. 客户端使用OSS SDK通过该临时访问凭证上传文件到OSS。

  6. OSS返回成功响应给客户端。

示例工程

sts.zip

示例代码

服务端代码示例

服务端生成临时访问凭证的代码示例如下:

import json
from alibabacloud_tea_openapi.models import Config
from alibabacloud_sts20150401.client import Client as Sts20150401Client
from alibabacloud_sts20150401 import models as sts_20150401_models
from alibabacloud_credentials.client import Client as CredentialClient

# 将替换为拥有上传文件到指定OSS Bucket权限的RAM角色的ARN。
role_arn_for_oss_upload = ''
# 将设置为STS服务的地域,例如cn-hangzhou。
region_id = ''

def get_sts_token():
    # 初始化 CredentialClient 时不指定参数,代表使用默认凭据链。
    # 在本地运行程序时,可以通过环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID、ALIBABA_CLOUD_ACCESS_KEY_SECRET 指定 AK;
    # 在 ECS\ECI\容器服务上运行时,可以通过环境变量 ALIBABA_CLOUD_ECS_METADATA 来指定绑定的实例\节点角色,SDK 会自动换取 STS 临时凭证。
    config = Config(region_id=region_id, credential=CredentialClient())
    sts_client = Sts20150401Client(config=config)
    assume_role_request = sts_20150401_models.AssumeRoleRequest(
        role_arn=role_arn_for_oss_upload,
        # 将设置为自定义的会话名称,例如oss-role-session。
        role_session_name=''
    )
    response = sts_client.assume_role(assume_role_request)
    token = json.dumps(response.body.credentials.to_map())
    return token

客户端代码示例

Web端使用临时访问凭证上传文件到OSS的代码示例如下:

let credentials = null;
const form = document.querySelector("form");
form.addEventListener("submit", async (event) => {
  event.preventDefault();
  // 临时凭证过期时,才重新获取,减少对 sts 服务的调用
  if (isCredentialsExpired(credentials)) {
    const response = await fetch("/get_sts_token_for_oss_upload", {
      method: "GET",
    });
    credentials = await response.json();
  }
  const client = new OSS({
    // 将设置为OSS Bucket名称。
    bucket: "",
    // 将设置为OSS Bucket所在地域,例如region: 'oss-cn-hangzhou'。
    region: "oss-",
    accessKeyId: credentials.AccessKeyId,
    accessKeySecret: credentials.AccessKeySecret,
    stsToken: credentials.SecurityToken,
  });

  const fileInput = document.querySelector("#file");
  const file = fileInput.files[0];
  const result = await client.put(file.name, file);
  console.log(result);
});

/**
 * 判断临时凭证是否到期。
 **/
function isCredentialsExpired(credentials) {
  if (!credentials) {
    return true;
  }
  const expireDate = new Date(credentials.Expiration);
  const now = new Date();
  // 如果有效期不足一分钟,视为过期。
  return expireDate.getTime() - now.getTime() <= 60000;
}

服务端生成Post签名和Post Policy

服务端通过Post签名和Post Policy授权客户端上传文件到OSS的过程如下。

阿里云对象存储OSS客户端直传概述-云淘科技

  1. 客户端向业务服务器请求Post签名和Post Policy等信息。

  2. 业务服务器生成并返回Post签名和Post Policy等信息给客户端。

  3. 客户端使用Post签名和Post Policy等信息调用PostObject通过HTML表单的方式上传文件到OSS。

  4. OSS返回成功响应给客户端。

示例工程

postsignature.zip

示例代码

服务端示例代码

服务端生成Post签名和Post Policy等信息的代码示例如下:

import os
from hashlib import sha1 as sha
import json
import base64
import hmac
import datetime
import time

# 配置环境变量OSS_ACCESS_KEY_ID。
access_key_id = os.environ.get('OSS_ACCESS_KEY_ID')
# 配置环境变量OSS_ACCESS_KEY_SECRET。
access_key_secret = os.environ.get('OSS_ACCESS_KEY_SECRET')
# 将替换为Bucket名称。
bucket = ''
# host的格式为bucketname.endpoint。将替换为Bucket名称。将替换为OSS Endpoint,例如oss-cn-hangzhou.aliyuncs.com。
host = 'https://.'
# 指定上传到OSS的文件前缀。
upload_dir = 'user-dir-prefix/'
# 指定过期时间,单位为秒。
expire_time = 3600


def generate_expiration(seconds):
    """
    通过指定有效的时长(秒)生成过期时间。
    :param seconds: 有效时长(秒)。
    :return: ISO8601 时间字符串,如:"2014-12-01T12:00:00.000Z"。
    """
    now = int(time.time())
    expiration_time = now + seconds
    gmt = datetime.datetime.utcfromtimestamp(expiration_time).isoformat()
    gmt += 'Z'
    return gmt


def generate_signature(access_key_secret, expiration, conditions, policy_extra_props=None):
    """
    生成签名字符串Signature。
    :param access_key_secret: 有权限访问目标Bucket的AccessKeySecret。
    :param expiration: 签名过期时间,按照ISO8601标准表示,并需要使用UTC时间,格式为yyyy-MM-ddTHH:mm:ssZ。示例值:"2014-12-01T12:00:00.000Z"。
    :param conditions: 策略条件,用于限制上传表单时允许设置的值。
    :param policy_extra_props: 额外的policy参数,后续如果policy新增参数支持,可以在通过dict传入额外的参数。
    :return: signature,签名字符串。
    """
    policy_dict = {
        'expiration': expiration,
        'conditions': conditions
    }
    if policy_extra_props is not None:
        policy_dict.update(policy_extra_props)
    policy = json.dumps(policy_dict).strip()
    policy_encode = base64.b64encode(policy.encode())
    h = hmac.new(access_key_secret.encode(), policy_encode, sha)
    sign_result = base64.b64encode(h.digest()).strip()
    return sign_result.decode()

def generate_upload_params():
    policy = {
        # 有效期。
        "expiration": generate_expiration(expire_time),
        # 约束条件。
        "conditions": [
            # 未指定success_action_redirect时,上传成功后的返回状态码,默认为 204。
            ["eq", "$success_action_status", "200"],
            # 表单域的值必须以指定前缀开始。例如指定key的值以user/user1开始,则可以写为["starts-with", "$key", "user/user1"]。
            ["starts-with", "$key", upload_dir],
            # 限制上传Object的最小和最大允许大小,单位为字节。
            ["content-length-range", 1, 1000000],
            # 限制上传的文件为指定的图片类型
            ["in", "$content-type", ["image/jpg", "image/png"]]
        ]
    }
    signature = generate_signature(access_key_secret, policy.get('expiration'), policy.get('conditions'))
    response = {
        'policy': base64.b64encode(json.dumps(policy).encode('utf-8')).decode(),
        'ossAccessKeyId': access_key_id,
        'signature': signature,
        'host': host,
        'dir': upload_dir
        # 可以在这里再自行追加其他参数
    }
    return json.dumps(response)

客户端示例代码

Web端使用Post签名和Post Policy等信息上传文件到OSS的代码示例如下:

const form = document.querySelector('form');
const fileInput = document.querySelector('#file');
form.addEventListener('submit', (event) => {
  event.preventDefault();
  let file = fileInput.files[0];
  let filename = fileInput.files[0].name;
  fetch('/get_post_signature_for_oss_upload', { method: 'GET' })
    .then(response => response.json())
    .then(data => {
      const formData = new FormData();
      formData.append('name',filename);
      formData.append('policy', data.policy);
      formData.append('OSSAccessKeyId', data.ossAccessKeyId);
      formData.append('success_action_status', '200');
      formData.append('signature', data.signature);
      formData.append('key', data.dir + filename);
      // file必须为最后一个表单域,除file以外的其他表单域无顺序要求。
      formData.append('file', file);
      fetch(data.host, { method: 'POST', body: formData },).then((res) => {
        console.log(res);
        alert('文件已上传');
      });
    })
    .catch(error => {
      console.log('Error occurred while getting OSS upload parameters:', error);
    });
});

服务端生成签名URL

服务端通过签名URL授权客户端上传文件到OSS的过程如下。

阿里云对象存储OSS客户端直传概述-云淘科技

  1. 客户端向业务服务器请求签名URL。

  2. 业务服务器使用OSS SDK生成PUT类型的签名URL,然后将其返回给客户端。

  3. 客户端使用PUT类型的签名URL调用PutObject上传文件到OSS。

  4. OSS向客户端返回成功响应。

示例工程

presignedurl.zip

示例代码

服务端示例代码

服务端生成签名URL的代码示例如下:

import oss2
from oss2.credentials import EnvironmentVariableCredentialsProvider

# 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
auth = oss2.ProviderAuth(EnvironmentVariableCredentialsProvider())
# 将替换为Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。
# 将替换为Bucket名称。
bucket = oss2.Bucket(auth, '', '')
# 指定过期时间,单位秒。
expire_time = 3600
# 填写Object完整路径,例如exampledir/exampleobject.png。Object完整路径中不能包含Bucket名称。
object_name = 'exampledir/exampleobject.png'

def generate_presigned_url():
    # 指定Header。
    headers = dict()
    # 指定Content-Type。
    headers['Content-Type'] = 'image/png'
    # 指定存储类型。
    # headers["x-oss-storage-class"] = "Standard"
    # 生成签名URL时,OSS默认会对Object完整路径中的正斜线(/)进行转义,从而导致生成的签名URL无法直接使用。
    # 设置slash_safe为True,OSS不会对Object完整路径中的正斜线(/)进行转义,此时生成的签名URL可以直接使用。
    url = bucket.sign_url('PUT', object_name, expire_time, slash_safe=True, headers=headers)
    return url

客户端示例代码

Web端使用签名URL上传文件到OSS的代码示例如下:

const form = document.querySelector("form");
form.addEventListener("submit", (event) => {
  event.preventDefault();
  const fileInput = document.querySelector("#file");
  const file = fileInput.files[0];
  fetch(`/get_presigned_url_for_oss_upload?filename=${file.name}`, { method: "GET" })
    .then((response) => {
        return response.text();
     })
    .then((url) => {
      fetch(url, {
        method: "PUT",
        headers: new Headers({
          'Content-Type': 'image/png',
        }),
        body: file,
       }).then((res) => {
            console.log(res);
            alert('文件已上传');
       });
   });
});

客户端直传实践参考

不同类型的客户端的直传实践参考如下:

  • Web端直传实践

  • 小程序直传实践

  • 移动应用端直传实践

内容没看懂? 不太想学习?想快速解决? 有偿解决: 联系专家

阿里云企业补贴进行中: 马上申请

腾讯云限时活动1折起,即将结束: 马上收藏

同尘科技为腾讯云授权服务中心。

购买腾讯云产品享受折上折,更有现金返利:同意关联,立享优惠

转转请注明出处:https://www.yunxiaoer.com/157054.html

(0)
上一篇 2023年12月10日
下一篇 2023年12月10日
详情页2

相关推荐

  • 阿里云对象存储OSS0002-00000039-云淘科技

    问题描述 单个请求不能同时在Header以及URL请求参数中均携带签名。 问题原因 您发起了携带签名的请求,在请求中携带了Authorization请求头的同时又在URL中携带了用于签名认证的参数,例如Expires、Signature、OSSAccessKeyId等。 问题示例 您发起了基于签名的认证请求,并且同时在Header和URL请求参数中携带了签名…

    阿里云对象存储 2023年12月10日
  • 阿里云对象存储OSSGo简单上传-云淘科技

    简单上传是指通过PutObject方法上传单个文件(Object)。简单上传包括流式上传和文件上传,流式上传使用文件流、网络流等作为OSS文件的数据源,文件上传使用本地文件作为OSS文件的数据源。本文介绍如何使用流式上传和文件上传方式上传文件。 注意事项 本文以华东1(杭州)外网Endpoint为例。如果您希望通过与OSS同地域的其他阿里云产品访问OSS,请…

    阿里云对象存储 2023年12月10日
  • 阿里云人工智能平台PAI读CSV文件-云淘科技

    读CSV文件支持从OSS、HTTP、HDFS读取CSV类型的文件数据。本文为您介绍读CSV文件的参数配置。 使用限制 通过可视化方式使用时,支持的计算资源为MaxCompute或Flink。 通过PyAlink脚本方式使用时,需要使用PyAlink脚本组件进行代码调用,详情请参见PyAlink脚本。 前提条件 (可选)已完成OSS授权,详情请参见云产品依赖与…

    阿里云人工智能平台PAI 2023年12月10日
  • 阿里云日志服务SLS配置异常检测功能-云淘科技

    CloudLens for OSS已内置告警监控规则,支持快速开启常用的Bucket异常检测功能,帮助您及时发现并分析在产品使用过程中的不合理行为。本文介绍设置异常检测的相关操作。 创建告警 登录日志服务控制台。 在日志应用区域的云产品Lens页签中,单击CloudLens for OSS。 在左侧导航栏中,选择异常检测 > 告警管理。 在Bucket…

    2023年12月10日
  • 信息流广告,信息流部分建议宽度830px,只针对默认列表样式,顺序随机
  • 阿里云云原生大数据计算服务 MaxCompute读取以分区方式存储的OSS数据-云淘科技

    MaxCompute支持创建OSS外部表为分区表,访问OSS上以分区方式存储的数据,通过该方式可降低读取数据量并提升数据处理效率。本文为您介绍MaxCompute支持的OSS标准分区路径格式和自定义分区路径格式。 背景信息 创建OSS外部表后,MaxCompute会全量扫描OSS目录下的所有数据,包括子目录下的数据文件。但当数据量比较大时,对全目录扫描会产生…

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信
本站为广大会员提供阿里云、腾讯云、华为云、百度云等一线大厂的购买,续费优惠,保证底价,买贵退差。