上传文件
在 BOS 中,用户操作的基本数据单元是 Object。单个 Bucket 中的 Object 数量不限,单个 Object 最大允许存储 5TB 数据。Object 由 Key(名称)、Meta(元信息)、Data(数据)三部分组成。
BOS Python SDK 提供了丰富的文件上传能力:
- 简单上传
- 追加上传
- 分块上传
- 断点续传上传
- 抓取上传
- 获取上传进度
Object 的命名规范如下:
- 使用 UTF-8 编码。
- 长度必须在 1~1023 字节之间。
- 首字符不能为
/,且不能包含@字符(@用于图片处理接口)。
运行以下示例前,请先参考初始化完成 BOS Client 初始化,并将示例中的 bucket_name、object_key 替换为实际值。
简单上传
简单上传支持以指定文件、数据流、字符串三种形式上传 Object。put_object 系列接口均支持不超过 5GB 的 Object 上传,上传成功后 BOS 会在响应头中返回 Object 的 ETag 作为文件标识。
1# 以数据流形式上传,需自行计算数据长度 content_length
2# 可提供校验字段:content_md5/content_sha256/content_crc32/content_crc32c/content_crc64ecma
3# 不提供时 SDK 默认计算 crc32
4data = open(file_name, 'rb')
5bos_client.put_object(bucket_name, object_key, data, content_length)
6
7# 从字符串上传
8bos_client.put_object_from_string(bucket_name, object_key, string)
9
10# 从文件上传
11bos_client.put_object_from_file(bucket_name, object_key, file_name)
这些接口的可选参数:
| 参数 | 说明 |
|---|---|
content_type |
上传内容的类型。 |
content_md5 |
文件内容 MD5 校验值,设置后 BOS 会校验内容一致性,不一致则报错。 |
content_length |
数据长度(put_object_from_string 不含该参数)。 |
content_sha256 |
文件 SHA256 校验值。 |
content_crc32 |
Object 的 CRC32 校验值(IEEE 算法)。 |
content_crc32c |
Object 的 CRC32C 校验值(Castagnoli 算法)。 |
content_crc32c_flag |
是否开启 CRC32C 校验。该值会原样作为请求头 x-bce-content-crc32c-flag 发送,服务端仅在其等于字符串 "true" 时才开启校验,因此请传字符串 "true"(注意小写),而非 Python 的 True(会被转成 "True" 而不生效)。 |
content_crc64ecma |
Object 的 CRC64 校验值。 |
user_metadata |
用户自定义元信息,所有 User Meta 总大小不超过 2KB。 |
storage_class |
文件的存储类型。 |
user_headers |
用户自定义 header。 |
SDK 在 baidubce.utils 中提供了计算各校验值的内置工具:
1from baidubce import utils
2
3fp = open(file_name, 'rb')
4
5# 计算 content_md5(fp 必选,offset/length/buf_size 可选)
6content_md5 = utils.get_md5_from_fp(fp)
7bos_client.put_object(bucket_name, object_key, data, content_length, content_md5=content_md5)
8
9# 计算 content_sha256
10content_sha256 = utils.get_sha256_from_fp(fp)
11bos_client.put_object(bucket_name, object_key, data, content_length, content_sha256=content_sha256)
12
13# 计算 content_crc32
14content_crc32 = utils.get_crc32_from_fp(fp)
15bos_client.put_object(bucket_name, object_key, data, content_length, content_crc32=content_crc32)
16
17# 计算 content_crc32c(需同时传 content_crc32c_flag="true",注意为小写字符串)
18content_crc32c = utils.get_crc32c_from_fp(fp)
19bos_client.put_object(bucket_name, object_key, data, content_length,
20 content_crc32c=content_crc32c, content_crc32c_flag="true")
21
22# 计算 content_crc64ecma
23content_crc64ecma = utils.get_crc64_ecma_from_fp(fp)
24bos_client.put_object(bucket_name, object_key, data, content_length, content_crc64ecma=content_crc64ecma)
设置文件元信息
文件元信息分为 HTTP 标准属性(HTTP Headers)与用户自定义元信息两类。
设置 HTTP Header
上传时可通过 user_headers 自定义 Object 的 HTTP Header,常用的有:
| 名称 | 描述 |
|---|---|
Cache-Control |
Object 被下载时网页的缓存行为。 |
Content-Encoding |
内容编码转换方式。 |
Content-Disposition |
指示 MIME 用户代理如何显示附件、以及文件名。 |
Expires |
缓存过期时间。 |
1user_headers = {"Cache-Control": "no-cache"}
2
3# 从字符串上传带有特定 header 的 Object
4bos_client.put_object_from_string(bucket=bucket_name, key=object_key,
5 data=string, user_headers=user_headers)
6
7# 从文件上传带有特定 header 的 Object
8bos_client.put_object_from_file(bucket=bucket_name, key=object_key,
9 file_name=file_name, user_headers=user_headers)
用户自定义元信息
1user_metadata = {"name": "my-data"}
2
3bos_client.put_object_from_string(bucket=bucket_name, key=object_key,
4 data=string, user_metadata=user_metadata)
5
6bos_client.put_object_from_file(bucket=bucket_name, key=object_key,
7 file_name=file_name, user_metadata=user_metadata)
提示:
- 上例自定义了一个名为
name、值为my-data的元信息。- 下载该 Object 时,自定义元信息会一并返回。
- 一个 Object 可有多个自定义元信息,但所有 User Meta 总大小不能超过 2KB。
上传时设置存储类型
BOS 支持标准存储、低频存储、冷存储、归档存储等存储类型,上传时通过 storage_class 指定,默认为标准存储。baidubce.services.bos.storage_class 模块提供的常量如下:
| 存储类型 | 常量 |
|---|---|
| 标准存储 | STANDARD |
| 低频存储 | STANDARD_IA |
| 冷存储 | COLD |
| 归档存储 | ARCHIVE |
| 多 AZ 标准存储 | MAZ_STANDARD |
| 多 AZ 低频存储 | MAZ_STANDARD_IA |
| 多 AZ 冷存储 | MAZ_COLD |
1from baidubce.services.bos import storage_class
2
3# 从文件上传冷存储类型的 Object
4bos_client.put_object_from_file(bucket=bucket_name, key=object_key,
5 file_name=file_name, storage_class=storage_class.COLD)
6
7# 从文件上传归档存储类型的 Object
8bos_client.put_object_from_file(bucket=bucket_name, key=object_key,
9 file_name=file_name, storage_class=storage_class.ARCHIVE)
追加上传
简单上传创建的 Object 为 Normal 类型,不可追加写。对于日志、视频监控、视频直播等数据复写较频繁的场景,BOS 支持 AppendObject,以追加写方式上传文件。通过 AppendObject 创建的 Object 为 Appendable 类型,可对其追加数据。
注意:
- 每次追加的
offset必须等于当前 Object 的长度,否则服务端返回OffsetIncorrect错误;首次追加offset为 0。- 单次追加的数据大小不超过 5GB,追加后 Object 总大小最大可达 48.8TB。
- 归档存储类型、以及开启了版本控制的 Bucket 均不支持追加上传。
1from baidubce.services.bos import storage_class
2
3# 首次追加:offset=0
4response = bos_client.append_object(bucket_name=bucket_name, key=object_key,
5 data=data, content_md5=content_md5, content_length=content_length, offset=0)
6
7# 从响应中获取下次追加的位置
8next_offset = response.metadata.bce_next_append_offset
9
10# 继续追加
11bos_client.append_object(bucket_name=bucket_name, key=object_key,
12 data=next_data, content_md5=next_content_md5, content_length=next_content_length,
13 offset=next_offset)
14
15# 从字符串追加上传
16bos_client.append_object_from_string(bucket_name=bucket_name, key=object_key,
17 data=string, offset=offset, storage_class=storage_class.STANDARD)
分块上传
除简单上传外,BOS 还提供 Multipart Upload(分块上传)模式,适用于以下场景:
- 需要断点续传。
- 上传超过 5GB 的文件。
- 网络较差、与服务器连接经常断开。
- 需要流式上传,或上传前无法确定文件大小。
初始化分块上传
1upload_id = bos_client.initiate_multipart_upload(bucket_name, object_key).upload_id
该方法返回 InitMultipartUploadResponse,其中的 upload_id 标识本次上传事件。初始化时也可指定 user_headers(Cache-Control、Content-Encoding、Content-Disposition、Expires)或 storage_class:
1from baidubce.services.bos import storage_class
2
3bos_client.initiate_multipart_upload(bucket_name=bucket_name, key=object_key,
4 storage_class=storage_class.STANDARD_IA)
上传分块
1import os
2
3left_size = os.path.getsize(file_name)
4offset = 0
5part_number = 1
6part_list = []
7
8while left_size > 0:
9 # 每块 5MB
10 part_size = 5 * 1024 * 1024
11 if left_size < part_size:
12 part_size = left_size
13
14 response = bos_client.upload_part_from_file(
15 bucket_name, object_key, upload_id, part_number, part_size, file_name, offset)
16
17 left_size -= part_size
18 offset += part_size
19 part_list.append({
20 "partNumber": part_number,
21 "eTag": response.metadata.etag
22 })
23 part_number += 1
注意:
offset以字节为单位,为分块的起始偏移位置;part_size为每个分块的大小。- 除最后一个分块外,其余每个分块的大小不得小于 100KB,否则在调用
complete_multipart_upload时服务端返回EntityTooSmall错误。单个分块最大不超过 5GB。- Part 号码范围为 1~10000,超出范围时服务端返回
InvalidArgument错误。- 建议保存每个分块返回的 ETag 与 partNumber,用于后续完成分块上传。
完成分块上传
1bos_client.complete_multipart_upload(bucket_name, object_key, upload_id, part_list)
part_list 为 list,每个元素是包含 partNumber 和 eTag 的 dict,例如:
1[{'partNumber': 1, 'eTag': 'f1c9645dbc14efddc7d8a322685f26eb'},
2 {'partNumber': 2, 'eTag': '93b885adfe0da089cdf634904fd59f71'}]
取消分块上传
1bos_client.abort_multipart_upload(bucket_name, object_key, upload_id=upload_id)
列举分块上传事件与已上传分块
1# 列举未完成的分块上传事件(单次最多 1000 个,支持 prefix/delimiter 过滤)
2response = bos_client.list_multipart_uploads(bucket_name)
3for item in response.uploads:
4 print(item.upload_id)
5
6# 自动翻页列举全部未完成事件
7for item in bos_client.list_all_multipart_uploads(bucket_name):
8 print(item.upload_id)
9
10# 列举某次上传事件中已上传的分块(单次最多 1000 个)
11response = bos_client.list_parts(bucket_name, object_key, upload_id)
12for item in response.parts:
13 print(item.part_number)
14
15# 自动翻页列举全部已上传分块
16for item in bos_client.list_all_parts(bucket_name, object_key, upload_id=upload_id):
17 print(item.part_number)
注意:
list_parts按 partNumber 升序返回。由于网络传输可能出错,不建议用其结果直接生成complete_multipart_upload的分块列表。
封装分块上传
put_super_object_from_file 封装了 initiate_multipart_upload、upload_part_from_file、complete_multipart_upload,只需一次调用即可完成分块上传。不提供校验字段时 SDK 默认计算 crc32。
1import multiprocessing
2
3file_name = "/path/to/file.zip"
4result = bos_client.put_super_object_from_file(bucket_name, key, file_name,
5 chunk_size=5, thread_num=multiprocessing.cpu_count())
6if result:
7 print("Upload success!")
可选参数:
| 参数 | 类型 | 说明 |
|---|---|---|
chunk_size |
int | 分块大小,单位 MB。SDK 要求取值大于 0 且不超过 5120(5GB),超出该范围会抛出 BceClientError。不指定时按文件大小自适应(自适应时最小为 5MB),支持的最大文件为 48.8TB。 |
thread_num |
int | 上传线程池的线程数,默认等于 CPU 核数。 |
若需中途取消上传,可借助 UploadTaskHandle 的 cancel() 方法:
1import time
2import threading
3import multiprocessing
4from baidubce.services.bos.bos_client import UploadTaskHandle
5
6file_name = "/path/to/file.zip"
7upload_task_handle = UploadTaskHandle()
8t = threading.Thread(target=bos_client.put_super_object_from_file,
9 args=(bucket_name, key, file_name),
10 kwargs={
11 "chunk_size": 5,
12 "thread_num": multiprocessing.cpu_count(),
13 "uploadTaskHandle": upload_task_handle
14 })
15t.start()
16time.sleep(2)
17upload_task_handle.cancel()
18t.join()
断点续传上传
向 BOS 上传大文件时,若网络不稳定或程序崩溃,整个上传会失败且已上传部分作废。BOS 提供断点续传能力:
抓取上传
从指定 URL 抓取资源并存入指定 Bucket,需要对该 Bucket 有写权限,每次抓取一个 Object,默认同步抓取。
1from baidubce.services.bos.bos_client import FETCH_MODE_ASYNC
2from baidubce.services.bos import storage_class
3
4fetch_url = "https://example.com/resource"
5
6# 默认同步抓取
7bos_client.fetch_object(bucket_name, object_key, fetch_url)
8
9# 异步抓取
10response = bos_client.fetch_object(bucket_name, object_key, fetch_url,
11 fetch_mode=FETCH_MODE_ASYNC, storage_class=storage_class.COLD)
12print("jobId: {}, code: {}, message: {}".format(
13 response.job_id, response.code, response.message))
获取上传进度
简单上传、追加上传、分块上传均支持通过 progress_callback 参数获取上传进度。推荐使用工具类内置的 utils.default_progress_callback:
1from baidubce import utils
2
3# 简单上传
4bos_client.put_object_from_file(bucket_name, object_key, file_name,
5 progress_callback=utils.default_progress_callback)
6
7# 追加上传
8bos_client.append_object_from_string(bucket_name=bucket_name, key=object_key,
9 data=string, progress_callback=utils.default_progress_callback)
10
11# 分块上传
12bos_client.upload_part_from_file(bucket_name, key, upload_id, part_number,
13 part_size, file_name, offset, progress_callback=utils.default_progress_callback)
也可自定义回调函数:
1import sys
2
3def percentage(consumed_bytes, total_bytes):
4 """进度回调函数,计算并打印完成百分比"""
5 if total_bytes:
6 rate = int(100 * (float(consumed_bytes) / float(total_bytes)))
7 print('\r{0}%'.format(rate))
8 sys.stdout.flush()
9
10bos_client.put_object(bucket_name, object_key, data, content_length,
11 progress_callback=percentage)
单链接限速
BOS 支持在上传、下载时进行流量控制。限速值取值范围为 819200~838860800,单位为 bit/s(即 100KB/s~100MB/s),必须为数字,超出范围或不合法时服务端返回 400 错误。
1traffic_limit_speed = 819200 * 5
2
3# 简单上传限速
4bos_client.put_object_from_file(bucket_name, key, file_name, traffic_limit=traffic_limit_speed)
5
6# 分块上传限速
7bos_client.upload_part_from_file(bucket_name, key, upload_id, part_number,
8 part_size, file_name, offset, traffic_limit=traffic_limit_speed)
9
10# 拷贝限速
11bos_client.copy_object(source_bucket, source_key, target_bucket, target_key,
12 traffic_limit=traffic_limit_speed)
设置对象过期时间
支持设置/获取对象过期时间的接口包括 PutObject、CopyObject、MultipartUpload、MultipartCopy、GetObject、GetObjectMeta。通过 user_headers 中的 x-bce-object-expires(单位:天)设置:
1user_headers = {"x-bce-object-expires": 3}
2bos_client.put_object_from_file(bucket_name, key, file_name, user_headers=user_headers)
3
4response = bos_client.get_object_meta_data(bucket_name, key)
5print(response.metadata)
条件读写
上传类接口中,put_object 与 complete_multipart_upload 支持通过 cond_read_write 传入条件读写 header:
| 参数 | 说明 |
|---|---|
If-Match |
仅当 Object 的 ETag 与所给值一致时请求才成功,否则返回 412。 |
If-None-Match |
仅当 ETag 不一致时请求才成功,否则返回 412。取值为 "*" 时,仅当该 Object 不存在时请求才成功。 |
If-Unmodified-Since |
仅当 Object 在所给时间之后未被修改过时请求才成功,否则返回 412。(与 If-Match 同时存在时忽略本项。) |
1cond_read_write = {
2 "If-Match": "example-etag",
3 "If-None-Match": "another-etag",
4 "If-Unmodified-Since": "Wed, 21 Jul 2020 07:23:48 GMT"
5}
6bos_client.put_object_from_file(bucket_name, key, file_name, cond_read_write=cond_read_write)
方法说明
| 方法 | 返回值 | 说明 |
|---|---|---|
bos_client.put_object(bucket_name, key, data, content_length, ...) |
BceResponse |
以数据流上传 Object。 |
bos_client.put_object_from_string(bucket, key, data, ...) |
BceResponse |
从字符串上传 Object。 |
bos_client.put_object_from_file(bucket, key, file_name, ...) |
BceResponse |
从文件上传 Object。 |
bos_client.append_object(bucket_name, key, data, ...) |
BceResponse |
追加上传(数据流)。 |
bos_client.append_object_from_string(bucket_name, key, data, ...) |
BceResponse |
追加上传(字符串)。 |
bos_client.initiate_multipart_upload(bucket_name, key, ...) |
BceResponse |
初始化分块上传。 |
bos_client.upload_part_from_file(bucket_name, key, upload_id, ...) |
BceResponse |
上传单个分块。 |
bos_client.complete_multipart_upload(bucket_name, key, upload_id, part_list) |
BceResponse |
完成分块上传。 |
bos_client.abort_multipart_upload(bucket_name, key, upload_id) |
BceResponse |
取消分块上传。 |
bos_client.put_super_object_from_file(bucket_name, key, file_name, ...) |
bool |
封装的分块上传。 |
bos_client.fetch_object(bucket_name, key, url, ...) |
BceResponse |
从 URL 抓取资源上传。 |
评价此篇文章
