在 OSS 上读写文件
在 OSS 中操作文件,不再使用传统的 fopen()、fclose() 等函数,而是通过 RESTful 风格的 HTTP 请求来完成:写文件使用 PUT,读文件使用 GET,获取文件属性使用 HEAD,删除文件使用 DELETE。
在配置好 OSS Python 开发环境后,可以使用已声明的存储对象(例如 my_store)来创建并写入一个新文件(在 OSS 中称为 Object)。示例代码如下:
res = my_store.put_object(bucket_name, object_name, input_content, content_type)
其中,content_type 应根据文件类型填写 HTTP 协议中规定的 MIME 类型。例如,JPG 图片使用 image/jpeg,MP3 文件使用 audio/mpeg。具体定义可参考 RFC 2616。设置正确的 content_type 有助于其他互联网应用直接、正确地使用 OSS 上的文件。
读取一个已存在文件的代码:
res = my_store.get_object(bucket_name, object_name)
获取文件属性的代码:
res = my_store.head_object(bucket_name, object_name)
删除一个文件的代码:
res = my_store.delete_object(bucket_name, object_name)
通过以上四个简单的函数,你可以轻松地将基于传统文件系统的应用迁移到 OSS 云存储平台。
通过签名 URL 防盗链
OSS 提供优秀的网络带宽,常被用于开发图片、音乐、视频等网站和应用。防盗链是一个关键问题,这里介绍一种简单且安全的方法:使用签名 URL。
首先,确保你的 Bucket 权限设置为 private,即所有请求必须通过签名认证才被视为合法。然后,根据操作类型、目标 Bucket、目标 Object 以及超时时间,动态生成一个经过签名的 URL。获得此 URL 的授权用户可以在过期前执行相应操作。
生成签名 URL 的 Python 代码示例:
url = my_store.sign_url(method, bucket_name, object_name, timeout=60)
其中 method 可以是 PUT、GET、HEAD、DELETE 中的任意一种;timeout 参数是超时时间,单位为秒。通过上述方法计算得到的签名 URL 示例如下:
http://storage.aliyun.com/sharedata/lingyun.jpg?OSSAccessKeyId=y6h7nbcothehvcp7jlnwmrw9&Expires=1335084740&Signature=LZeqnHSo5WkDNWKffKDgQBXR6fY=
这种动态生成签名 URL 的方式,可以有效保护 OSS 上的数据,防止被他人盗链。
满足特定条件时才传输数据
OSS 支持四种条件传输参数,只有当 Object 的属性满足客户端给出的条件时,OSS 才会传输 Object 的数据。这四种参数是:
- If-Modified-Since
- If-Unmodified-Since
- If-Match
- If-None-Match
If-Modified-Since 是 HTTP 协议中常用的参数。服务器通过客户端提供的时间戳判断数据是否最新:如果不是最新,则返回服务器端数据;如果是最新,则返回 304 状态码,告知客户端可直接使用本地缓存。这能显著减少网络传输数据量并减轻服务器负担。
If-Unmodified-Since 的含义与 If-Modified-Since 相反:如果内容未更新,则返回数据;否则返回 304。If-Unmodified-Since 和 If-Modified-Since 可结合使用以指定一个时间窗口。示例:
headers = {}
headers['If-Modified-Since'] = "Sun, 22 Apr 2012 09:06:23 GMT"
headers['If-Unmodified-Since'] = "Sun, 22 Apr 2012 09:16:23 GMT"
res = my_store.get_object(bucket_name, object_name, headers)
If-Match 和 If-None-Match 这对参数与上述时间戳参数类似,但条件是基于内容的 ETag(通常是 MD5 值)。合理利用这四个参数可以节省大量流量,从而降低成本。
在 OSS 上实现文件夹功能
许多用户习惯文件夹的概念,而云存储的逻辑只有 Bucket 和 Object。我们可以在 OSS 上逻辑性地实现文件夹功能。
约定:所有以 / 结尾的 Object 被视为一个文件夹。例如,用户可将 folder 视为文件,将 folder/ 视为文件夹,而 folder/file.txt 则是位于 folder 文件夹内的文件。在 OSS 中,这三者都是独立的 Object。
当需要查询文件夹下的文件时,可通过 List Objects(Get Bucket)接口的四个参数巧妙实现:prefix、marker、delimiter 和 max-keys。
假设在名为 mydata 的 Bucket 内有以下文件:
lingyun.doc
folder/
folder/file1.txt
folder/file2.txt
folder/file3.txt
folder/image/
folder/image/test.jpg
若将此 Bucket 视为传统文件系统,用户进入后应只看到一个文件 lingyun.doc 和一个文件夹 folder/。为实现此效果,可将 List Objects 请求的 delimiter 参数设为 /:
res = my_store.list_objects("mydata", delimiter='/')
OSS 将返回一个 XML 格式的响应,其中包含一个 key 为 lingyun.doc 的文件,以及一个名为 folder 的 common prefix,分别对应文件和文件夹。
若要查看文件夹 folder 内的文件列表,可将 prefix 参数设为 folder/:
res = my_store.list_objects("mydata", prefix='folder/', delimiter='/')
执行后,可知文件夹 folder 内有三个文件:file1.txt、file2.txt、file3.txt,以及一个子文件夹 image/。
max-keys 参数定义了单次请求 OSS 返回文件和文件夹的最大数量,默认值为 100,最大可设为 1000。如果一个文件夹内的文件超过 1000 个,可使用 marker 参数。该参数指示 OSS 从指定文件开始,按字典序查询后续文件。示例:
res = my_store.list_objects("mydata", prefix='folder/', marker='folder/file1.txt', delimiter='/', maxkeys='1')
此时,OSS 仅返回一个查询结果:folder/file2.txt。灵活运用这四个参数,可以轻松构建类似 DropBox 的应用。
实现 Object 断点下载和并发下载
断点下载是下载功能的基础。其原理是记录上次接收数据的位置,然后要求服务器从断点处开始传输剩余部分。下载 OSS 上的 Object 时,可使用 HTTP 请求中通用的 Range 头部来实现。
获取文件头 5 个字节的请求代码:
headers = {}
headers['range'] = "bytes=0-4"
res = my_store.get_object(bucket_name, object_name, headers)
获取文件中间 3KB 字节数据的请求代码:
headers = {}
headers['range'] = "bytes=1024-4095"
res = my_store.get_object(bucket_name, object_name, headers)
掌握如何使用 range 随机读取 Object 后,实现并发下载就很简单了:将待下载的 Object 分成若干块,开启多个线程,每个线程下载一块。所有块下载完成后,整个文件即下载完毕。
注意:根据 HTTP 协议,如果请求中包含 range 字段,服务器返回的 HTTP 状态码为 206(Partial Content)。
实现大文件并发上传
由于 OSS 是互联网服务,用户终端很难长时间保持与 OSS 的 TCP 连接稳定,上传大文件时容易发生连接断开的情况。此时可采用 OSS 的 Multipart Upload 模式。
Multipart Upload 的原理是将一个大文件在客户端拆分为多个适合上传的小片(Part),分别上传至 OSS 服务器端,最后在服务器端组合成完整文件。每个小片独立上传,互不关联,因此可以实现并发上传。
虽然原理看似复杂,但使用 OSS 提供的 SDK 可以简化操作。例如,一行命令即可实现并发上传:
res = my_store.multi_upload_file(bucket_name, object_name, thread_num=10)
具体实现细节请参考 OSS API 开发文档和 SDK 内部逻辑。有兴趣的开发者可根据特定需求自行实现。
删除包含大量 Object 的 Bucket
当你尝试删除一个非空的 Bucket 时,OSS 会出于数据保护拒绝操作。如果 Bucket 内有成千上万个文件,逐个删除效率低下。此时可以先获取 Object 列表,再使用批量删除接口。Python SDK 已封装相应接口:
object_list = []
while True:
object_list = my_store.list_objects(bucket_name)
if len(object_list) != 0:
my_store.batch_delete_objects(bucket_name, object_list)
else:
break
这样,删除数万个文件也只需几十个请求,既节省了请求次数,也节约了时间。
为 Object 添加自定义 Header
有时我们希望为文件的元数据(META)添加自定义信息,例如照片的拍摄时间、文章的作者、歌曲的专辑名或专利号。这样在查看文件属性时即可获取这些信息,而无需下载整个文件。
在使用 OSS 时,可以通过在 PutObject 时将自定义信息放在以 x-oss-meta- 为前缀的参数中。OSS 会将这些参数视为用户自定义的元数据。添加 x-oss-meta-author 的示例代码:
headers = {}
headers['x-oss-meta-author'] = 'obama'
res = my_store.put_object(bucket_name, object_name, headers)
获取该 Object 时,将在 HTTP 响应头中看到类似以下信息:
HTTP/1.1 200 OK
x-oss-request-id: 3a89276f-2e2d-7965-3ff9-51c875b99c41
Date: Fri, 24 Feb 2012 06:38:30 GMT
Last-Modified: Fri, 24 Feb 2012 06:07:48 GMT
ETag: "5B3C1A2E053D763E1B002CC607C5A0FE"
Content-Type: image/jpg
Content-Length: 344606
X-oss-meta-author: obama
Server: AliyunOSS
[344606 bytes of object data]
在 OSS 上调试代码
使用 OSS 开发时,如果发送了非法或不符合规则的 HTTP 请求,OSS 会返回错误码和详细信息,帮助开发者发现和定位问题。对于所有 HTTP 返回码非 2XX 的请求,OSS 都会返回一个 XML 结构的消息体,详细说明无法执行请求的原因。
例如,尝试访问一个没有权限的 Object 时,OSS 会返回 403 Forbidden 错误码及如下 XML 消息体:
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>AccessDenied</Code>
<Message>Access denied.</Message>
<RequestId>17baec8b-1a0e-8dad-4a6e-343b4d8450dc</RequestId>
<HostId>storage.aliyun.com</HostId>
</Error>
其中 RequestId 字段是唯一标识该次请求的 UUID。若无法自行解决问题,可凭此 RequestId 向 OSS 技术支持寻求帮助。