You need to enable JavaScript to run this app.
导航

追加上传(Python SDK)

最近更新时间2024.02.04 18:30:54

首次发布时间2021.12.31 17:38:36

追加上传创建的对象类型为追加类型 (Appendable Object) ,可在对象末尾追加写入数据。而普通上传和分片上传创建的对象类型为普通类型 (Normal Object),无法追加写入数据。

注意事项

  • 追加上传对象前,您必须具有 tos:PutObject 权限,具体操作请参见权限配置指南
  • 上传对象时,对象名必须满足一定规范,详细信息请参见对象命名规范
  • TOS 是面向海量存储设计的分布式对象存储产品,内部分区存储了对象索引数据,为横向扩展您上传对象和下载对象时的最大吞吐量,和减小热点分区的概率,请您避免使用字典序递增的对象命名方式,详细信息,请参见性能优化
  • 如果桶中已经存在同名对象,则新对象会覆盖已有的对象。桶开启多版本的场景下,则会保留原有对象,生成一个新版本号用于标识新上传的对象。
  • 追加上传对象不支持 Chunk-Encoded 的请求方式,当您追加上传网络流时请迭代获取数据再追加上传。

限制说明

使用追加上传时, 限制条件如下:

  • 追加对象大小不能大于 5GiB。
  • 通过追加上传创建的对象,进行普通上传时,对象被覆盖且对象类型由追加类型变为普通类型。反之通过普通上传的对象不支持追加写操作。
  • 追加上传创建的对象不支持拷贝。
  • 如果您的存储桶处于开启或者暂停多版本功能的状态下,或存储桶的类型为低频存储,则无法追加写入对象。

示例代码

追加上传字符流

以下代码用于将字符流追加上传到目标桶 bucket-test 中的 object-test 对象。

from io import StringIO

import os
import tos

from tos import RateLimiter

# 从环境变量获取 AK 和 SK 信息。
ak = os.getenv('TOS_ACCESS_KEY')
sk = os.getenv('TOS_SECRET_KEY')
# your endpoint 和 your region 填写Bucket 所在区域对应的Endpoint。# 以华北2(北京)为例,your endpoint 填写 tos-cn-beijing.volces.com,your region 填写 cn-beijing。
endpoint = "your endpoint"
region = "your region"
bucket_name = "bucket-test"
object_key = "object-test"
try:
    client = tos.TosClientV2(ak, sk, endpoint, region)
    # 创建类型为可追加文件, 首次偏移 offset 设置为0
    # 通过 acl=tos.ACLType.ACL_Private 参数设置对象的访问权限为私有
    # 通过 storage_class=tos.StorageClassType.Storage_Class_Standard 设置对象的存储类型
    content_1 = 'Hello TOS A'
    content_2 = 'Hello TOS B'
    content_3 = 'Hello TOS C'

    # 追加数据, 偏移量从上次响应中获取<next_append_offset>,或者通过 head_object 返回的 content-length获取
    # 创建类型为可追加文件,首次偏移 offset 设置为0
    # 通过 acl=tos.ACLType.ACL_Private 参数设置对象的访问权限为私有
    # 通过 storage_class=tos.StorageClassType.Storage_Class_Standard 设置对象的存储类型
    # 第一次追加写入offset=0,设置pre_hash_crc64_ecma为0
    # 传入 pre_hash_crc64_ecma 保障了append对象数据完整性
    result_1 = client.append_object(bucket_name, object_key, 0, content=StringIO(content_1), pre_hash_crc64_ecma=0)

    # 第二次追加, 偏移量从上次响应中获取<next_append_offset>,或者通过 head_object 返回的 content-length获取。
    # 设置offset=next_append_offset,设置pre_hash_crc64_ecma为上传响应中的hash_crc64_ecma
    # 传入 pre_hash_crc64_ecma 保障了append对象数据完整性
    result_2 = client.append_object(bucket_name, object_key, result_1.next_append_offset, content=StringIO(content_2),
                                    pre_hash_crc64_ecma=result_1.hash_crc64_ecma)

    # 第三次次追加, 偏移量从上次响应中获取<next_append_offset>,或者通过 head_object 返回的 content-length获取。
    # 设置offset=next_append_offset,设置pre_hash_crc64_ecma为上传响应中的hash_crc64_ecma
    # 传入 pre_hash_crc64_ecma 保障了append对象数据完整性
    result_3 = client.append_object(bucket_name, object_key, result_2.next_append_offset, content=StringIO(content_3),
                                    pre_hash_crc64_ecma=result_2.hash_crc64_ecma)
except tos.exceptions.TosClientError as e:
    # 操作失败,捕获客户端异常,一般情况为非法请求参数或网络异常
    print('fail with client error, message:{}, cause: {}'.format(e.message, e.cause))
except tos.exceptions.TosServerError as e:
    # 操作失败,捕获服务端异常,可从返回信息中获取详细错误信息
    print('fail with server error, code: {}'.format(e.code))
    # request id 可定位具体问题,强烈建议日志中保存
    print('error with request id: {}'.format(e.request_id))
    print('error with message: {}'.format(e.message))
    print('error with http code: {}'.format(e.status_code))
    print('error with ec: {}'.format(e.ec))
    print('error with request url: {}'.format(e.request_url))
except Exception as e:
    print('fail with unknown error: {}'.format(e))

追加上传 Bytes 流

以下代码用于将 Bytes 流追加上传到目标桶 bucket-test 中的 object-test 对象。

from io import BytesIO

import os
import tos


# 从环境变量获取 AK 和 SK 信息。
ak = os.getenv('TOS_ACCESS_KEY')
sk = os.getenv('TOS_SECRET_KEY')
# your endpoint 和 your region 填写Bucket 所在区域对应的Endpoint。# 以华北2(北京)为例,your endpoint 填写 tos-cn-beijing.volces.com,your region 填写 cn-beijing。
endpoint = "your endpoint"
region = "your region"
bucket_name = "bucket-test"
object_key = "object-test"
try:
    # 创建 TosClientV2 对象,对桶和对象的操作都通过 TosClientV2 实现
    client = tos.TosClientV2(ak, sk, endpoint, region)
    # 创建类型为可追加文件,首次偏移 offset 设置为0
    # 通过 acl=tos.ACLType.ACL_Private 参数设置对象的访问权限为私有
    # 通过 storage_class=tos.StorageClassType.Storage_Class_Standard 设置对象的存储类型
    content_1 = b'Hello TOS A'
    content_2 = b'Hello TOS B'
    content_3 = b'Hello TOS C'

    # 追加数据, 偏移量从上次响应中获取<next_append_offset>,或者通过 head_object 返回的 content-length获取
    # 创建类型为可追加文件,首次偏移 offset 设置为0
    # 通过 acl=tos.ACLType.ACL_Private 参数设置对象的访问权限为私有
    # 通过 storage_class=tos.StorageClassType.Storage_Class_Standard 设置对象的存储类型
    # 第一次追加写入offset=0,无需设置pre_hash_crc64_ecma
    # 传入 pre_hash_crc64_ecma 保障了append对象数据完整性
    result_1 = client.append_object(bucket_name, object_key, 0, content=BytesIO(content_1), pre_hash_crc64_ecma=0)

    # 第二次追加, 偏移量从上次响应中获取<next_append_offset>,或者通过 head_object 返回的 content-length获取。
    # 设置offset=next_append_offset,设置pre_hash_crc64_ecma为上传响应中的hash_crc64_ecma
    # 传入 pre_hash_crc64_ecma 保障了append对象数据完整性
    result_2 = client.append_object(bucket_name, object_key, result_1.next_append_offset, content=BytesIO(content_2),
                                    pre_hash_crc64_ecma=result_1.hash_crc64_ecma)

    # 第三次次追加, 偏移量从上次响应中获取<next_append_offset>,或者通过 head_object 返回的 content-length获取。
    # 设置offset=next_append_offset,设置pre_hash_crc64_ecma为上传响应中的hash_crc64_ecma
    # 传入 pre_hash_crc64_ecma 保障了append对象数据完整性
    result_3 = client.append_object(bucket_name, object_key, result_2.next_append_offset, content=BytesIO(content_3),
                                    pre_hash_crc64_ecma=result_2.hash_crc64_ecma)
except tos.exceptions.TosClientError as e:
    # 操作失败,捕获客户端异常,一般情况为非法请求参数或网络异常
    print('fail with client error, message:{}, cause: {}'.format(e.message, e.cause))
except tos.exceptions.TosServerError as e:
    # 操作失败,捕获服务端异常,可从返回信息中获取详细错误信息
    print('fail with server error, code: {}'.format(e.code))
    # request id 可定位具体问题,强烈建议日志中保存
    print('error with request id: {}'.format(e.request_id))
    print('error with message: {}'.format(e.message))
    print('error with http code: {}'.format(e.status_code))
    print('error with ec: {}'.format(e.ec))
    print('error with request url: {}'.format(e.request_url))
except Exception as e:
    print('fail with unknown error: {}'.format(e))

追加上传网络流

以下代码用于将网络流追加上传到目标桶 bucket-test 中的 object-test 对象。

import requests
import os
import tos


# 从环境变量获取 AK 和 SK 信息。
ak = os.getenv('TOS_ACCESS_KEY')
sk = os.getenv('TOS_SECRET_KEY')
# your endpoint 和 your region 填写Bucket 所在区域对应的Endpoint。# 以华北2(北京)为例,your endpoint 填写 tos-cn-beijing.volces.com,your region 填写 cn-beijing。
endpoint = "your endpoint"
region = "your region"
bucket_name = "bucket-test"
object_key = "object-test"
try:
    client = tos.TosClientV2(ak, sk, endpoint, region)
    next_append_offset = 0
    pre_hash_crc64_ecma = 0
    stream = requests.get('https://www.volcengine.com')
    for chunk in stream:
        # next_append_offset为偏移量,可从上次响应中获取<next_append_offset>,或者通过 head_object 返回的 <content-length>获取
        # pre_hash_crc64_ecma 当前数据的crc64值,可从上此响应中获取<hash_crc64_ecma>, 或通过head_object返回的 <hash_crc64_ecma>获取
        # 传入 pre_hash_crc64_ecma 保障了append对象数据完整性
        result = client.append_object(bucket_name, object_key, next_append_offset, content=chunk,
                                      pre_hash_crc64_ecma=pre_hash_crc64_ecma)
        next_append_offset = result.next_append_offset
        pre_hash_crc64_ecma = result.hash_crc64_ecma
    stream.close()
except tos.exceptions.TosClientError as e:
    # 操作失败,捕获客户端异常,一般情况为非法请求参数或网络异常
    print('fail with client error, message:{}, cause: {}'.format(e.message, e.cause))
except tos.exceptions.TosServerError as e:
    # 操作失败,捕获服务端异常,可从返回信息中获取详细错误信息
    print('fail with server error, code: {}'.format(e.code))
    # request id 可定位具体问题,强烈建议日志中保存
    print('error with request id: {}'.format(e.request_id))
    print('error with message: {}'.format(e.message))
    print('error with http code: {}'.format(e.status_code))
    print('error with ec: {}'.format(e.ec))
    print('error with request url: {}'.format(e.request_url))
except Exception as e:
    print('fail with unknown error: {}'.format(e))

进度条处理

说明

对于字符串、Bytes 和本地文件四种形式的数据,支持进度条功能。网络流等类型数据无法获取上传内容的大小,因此回调时不会返回对象总体大小。

以下代码用于向追加上传过程中添加进度条功能。

import os
import tos

from tos import DataTransferType

# 从环境变量获取 AK 和 SK 信息。
ak = os.getenv('TOS_ACCESS_KEY')
sk = os.getenv('TOS_SECRET_KEY')
# your endpoint 和 your region 填写Bucket 所在区域对应的Endpoint。# 以华北2(北京)为例,your endpoint 填写 tos-cn-beijing.volces.com,your region 填写 cn-beijing。
endpoint = "your endpoint"
region = "your region"
bucket_name = "bucket-test"
object_key = "object-test"
try:
    def percentage(consumed_bytes: int, total_bytes: int, rw_once_bytes: int, type: DataTransferType):
        if total_bytes:
            rate = int(100 * float(consumed_bytes) / float(total_bytes))
            print("rate:{}, consumed_bytes:{},total_bytes{}, rw_once_bytes:{}, type:{}".format(rate, consumed_bytes,
                                                                                               total_bytes,
                                                                                               rw_once_bytes, type))


    client = tos.TosClientV2(ak, sk, endpoint, region)
    # data_transfer_listener 为可选参数, 用于实现进度条功能。
    client.append_object(bucket_name, object_key, 0, content='a' * 1024 * 1024, data_transfer_listener=percentage)
except tos.exceptions.TosClientError as e:
    # 操作失败,捕获客户端异常,一般情况为非法请求参数或网络异常
    print('fail with client error, message:{}, cause: {}'.format(e.message, e.cause))
except tos.exceptions.TosServerError as e:
    # 操作失败,捕获服务端异常,可从返回信息中获取详细错误信息
    print('fail with server error, code: {}'.format(e.code))
    # request id 可定位具体问题,强烈建议日志中保存
    print('error with request id: {}'.format(e.request_id))
    print('error with message: {}'.format(e.message))
    print('error with http code: {}'.format(e.status_code))
    print('error with ec: {}'.format(e.ec))
    print('error with request url: {}'.format(e.request_url))
except Exception as e:
    print('fail with unknown error: {}'.format(e))

客户端限速

以下代码用于追加上传过程中客户端限速。

import os
import tos


# 从环境变量获取 AK 和 SK 信息。
ak = os.getenv('TOS_ACCESS_KEY')
sk = os.getenv('TOS_SECRET_KEY')
# your endpoint 和 your region 填写Bucket 所在区域对应的Endpoint。# 以华北2(北京)为例,your endpoint 填写 tos-cn-beijing.volces.com,your region 填写 cn-beijing。
endpoint = "your endpoint"
region = "your region"
bucket_name = "bucket-test"
object_key = "object-test"
try:
    client = tos.TosClientV2(ak, sk, endpoint, region)
    # rate_limiter 为可选参数, 用于实现客户端限速。
    # TOS Python SDK 通过最基本的令牌桶算法实现了客户端限速,其中rate为发送令牌的速率,capacity为总容量
    # 以下配置的意义为5mb/s的平均上传速率,最大支持 10 + 5 mb/s的上传速率
    rate_limiter = tos.RateLimiter(rate=1024 * 1024 * 5, capacity=10 * 1024 * 1024)
    client.append_object(bucket_name, object_key, 0, content='a' * 1024 * 1024, rate_limiter=rate_limiter)
except tos.exceptions.TosClientError as e:
    # 操作失败,捕获客户端异常,一般情况为非法请求参数或网络异常
    print('fail with client error, message:{}, cause: {}'.format(e.message, e.cause))
except tos.exceptions.TosServerError as e:
    # 操作失败,捕获服务端异常,可从返回信息中获取详细错误信息
    print('fail with server error, code: {}'.format(e.code))
    # request id 可定位具体问题,强烈建议日志中保存
    print('error with request id: {}'.format(e.request_id))
    print('error with message: {}'.format(e.message))
    print('error with http code: {}'.format(e.status_code))
    print('error with ec: {}'.format(e.ec))
    print('error with request url: {}'.format(e.request_url))
except Exception as e:
    print('fail with unknown error: {}'.format(e))

相关文档

关于追加上传的 API 文档,请参见 AppendObject