Commit ad17753418f454ec63a00d2647e073967c54d587
0 parents
Exists in
master
OSS from Aliyun
Showing 11 changed files with 4835 additions and 0 deletions Inline Diff
.gitignore
| File was created | 1 | *.pyc | ||
| 2 | .venv | |||
| 3 | .idea | |||
| 4 | *.iml | |||
| 5 | *.egg-info |
OSS_Python_SDK.pdf
No preview for this file type
README
| File was created | 1 | 阿里云开放存储服务 Open Storage Service (OSS) Python SDK说明文档 | ||
| 2 | =============================================================== | |||
| 3 | 阿里云开放存储服务官方网站: | |||
| 4 | http://oss.aliyun.com | |||
| 5 | ||||
| 6 | 阿里云开放存储 | |||
| 7 | =============================================================== | |||
| 8 | 存储在OSS里的文件叫做"object". 所有的object都放在bucket里面。 | |||
| 9 | ||||
| 10 | 简介 | |||
| 11 | =============================================================== | |||
| 12 | 这篇文档主要介绍如何使用Python来进行OSS API调用,并且介绍osscmd | |||
| 13 | 的简单使用。 | |||
| 14 | 这篇文档假设你已经熟悉Python,熟悉OSS的相关概念,并且已经注册了 | |||
| 15 | 阿里云的OSS服务,且获得了相应的ID和KEY。 | |||
| 16 | 如果你还没有开通或者还不了解OSS,请移步OSS官方网站。 | |||
| 17 | ||||
| 18 | 环境要求 | |||
| 19 | =============================================================== | |||
| 20 | Python SDK需要:安装python 2.5(包括)以上且在3.0(不包括)以下 | |||
| 21 | 的版本。 | |||
| 22 | 可以在Windows平台和Linux平台使用。 | |||
| 23 | ||||
| 24 | 如何获取 | |||
| 25 | =============================================================== | |||
| 26 | 1. 打开浏览器,输入oss.aliyun.com | |||
| 27 | 2. 找到Python SDK链接: | |||
| 28 | 3. 下载后可以得到类似OSS_Python_API_xxxxxxxx.tar.gz的包 | |||
| 29 | 4. 进入压缩包所在的目录,进行解压缩 | |||
| 30 | 5. 解压缩后得到,oss文件夹和osscmd文件 | |||
| 31 | ||||
| 32 | 使用说明 | |||
| 33 | =============================================================== | |||
| 34 | 使用oss_api.py | |||
| 35 | =============================================================== | |||
| 36 | 1. 创建bucket | |||
| 37 | def put_bucket(self, bucket, acl='', headers=None): | |||
| 38 | 等同create_bucket函数 | |||
| 39 | def create_bucket(self, bucket, acl='', headers=None): | |||
| 40 | 参数说明: | |||
| 41 | bucket,类型:string | |||
| 42 | acl,类型:string,目前为private,public-read, | |||
| 43 | public-read-write中的一种 | |||
| 44 | headers, 类型:dict,默认为空 | |||
| 45 | 返回值说明: | |||
| 46 | HTTP Response | |||
| 47 | 参见http://docs.python.org/2/library/httplib.html | |||
| 48 | def put_bucket_with_location(self, bucket, acl='', \ | |||
| 49 | location='', headers=None): | |||
| 50 | 参数说明: | |||
| 51 | bucket,类型:string | |||
| 52 | acl,类型:string | |||
| 53 | location, 类型:string | |||
| 54 | headers, 类型:dict | |||
| 55 | 返回值说明: | |||
| 56 | HTTP Response | |||
| 57 | 2. 删除bucket | |||
| 58 | def delete_bucket(self, bucket, headers=None): | |||
| 59 | 参数说明: | |||
| 60 | bucket,类型:string | |||
| 61 | headers, 类型:dict | |||
| 62 | 返回值说明: | |||
| 63 | HTTP Response | |||
| 64 | 3. 修改bucket访问权限 | |||
| 65 | def put_bucket(self, bucket, acl='', headers=None): | |||
| 66 | def create_bucket(self, bucket, acl='', headers=None): | |||
| 67 | 同1中的put_bucket和create_bucket | |||
| 68 | 4. 获取bucket访问权限 | |||
| 69 | def get_bucket_acl(self, bucket): | |||
| 70 | 参数说明: | |||
| 71 | bucket,类型:string | |||
| 72 | 返回值说明: | |||
| 73 | HTTP Response | |||
| 74 | 5. 显示创建的bucket | |||
| 75 | def get_service(self, headers=None): | |||
| 76 | 参数说明: | |||
| 77 | headers, 类型:dict | |||
| 78 | 返回值说明: | |||
| 79 | HTTP Response | |||
| 80 | def list_all_my_buckets(self, headers=None): | |||
| 81 | 参数说明: | |||
| 82 | headers, 类型:dict | |||
| 83 | 返回值说明: | |||
| 84 | HTTP Response | |||
| 85 | 6. 上传object | |||
| 86 | def put_object_from_string(self, bucket, object,\ | |||
| 87 | input_content,\ | |||
| 88 | content_type=DefaultContentType,\ | |||
| 89 | headers=None, params=None): | |||
| 90 | 参数说明: | |||
| 91 | bucket,类型:string | |||
| 92 | object,类型:string | |||
| 93 | input_content,类型:string | |||
| 94 | content_type,类型:string | |||
| 95 | headers,类型:dict | |||
| 96 | params,类型:dict | |||
| 97 | 返回值说明: | |||
| 98 | HTTP Response | |||
| 99 | def put_object_from_file(self, bucket, object,\ | |||
| 100 | filename, content_type='',\ | |||
| 101 | headers=None, params=None): | |||
| 102 | 参数说明: | |||
| 103 | bucket,类型:string | |||
| 104 | object,类型:string | |||
| 105 | filename,类型:string,本地需要上传的文件名 | |||
| 106 | content_type,类型:string,object的类型 | |||
| 107 | headers,类型:dict | |||
| 108 | params,类型:dict | |||
| 109 | 返回值说明: | |||
| 110 | HTTP Response | |||
| 111 | 7. 显示上传的object | |||
| 112 | def get_bucket(self, bucket, prefix='', marker='',\ | |||
| 113 | delimiter='', maxkeys='', headers=None): | |||
| 114 | 同list_bucket | |||
| 115 | def list_bucket(self, bucket, prefix='', marker='',\ | |||
| 116 | delimiter='', maxkeys='', headers=None): | |||
| 117 | 参数说明: | |||
| 118 | bucket,类型:string | |||
| 119 | prefix,类型:string | |||
| 120 | marker,类型:string | |||
| 121 | delimiter,类型:string | |||
| 122 | maxkeys,类型:string | |||
| 123 | headers,类型:dict | |||
| 124 | 返回值说明: | |||
| 125 | HTTP Response | |||
| 126 | 8. 删除object | |||
| 127 | def delete_object(self, bucket, object, headers=None): | |||
| 128 | 参数说明: | |||
| 129 | bucket,类型:string | |||
| 130 | object,类型:string | |||
| 131 | headers,类型:dict | |||
| 132 | 返回值说明: | |||
| 133 | HTTP Response | |||
| 134 | 9. 下载object | |||
| 135 | def get_object_to_file(self, bucket, object,\ | |||
| 136 | filename, headers=None): | |||
| 137 | 参数说明: | |||
| 138 | bucket,类型:string | |||
| 139 | object,类型:string | |||
| 140 | filename,类型:string, | |||
| 141 | 将object内容下载到本地文件的文件名 | |||
| 142 | headers,类型:dict | |||
| 143 | 返回值说明: | |||
| 144 | HTTP Response | |||
| 145 | 10. 使用示例: | |||
| 146 | 在解压的oss目录下,创建一个测试文件test.py内容如下, | |||
| 147 | 并将ACCESS_ID和SECRET_ACCESS_KEY的内容填写正确, | |||
| 148 | 并且将BUCKET填写一个唯一的名字。 | |||
| 149 | ||||
| 150 | #!/usr/bin/env python | |||
| 151 | #coding=utf8 | |||
| 152 | import time | |||
| 153 | from oss_api import * | |||
| 154 | from oss_xml_handler import * | |||
| 155 | HOST="oss.aliyuncs.com" | |||
| 156 | ACCESS_ID = "" | |||
| 157 | SECRET_ACCESS_KEY = "" | |||
| 158 | #ACCESS_ID and SECRET_ACCESS_KEY 默认是空, | |||
| 159 | #请填入您申请的正确的ID和KEY. | |||
| 160 | BUCKET = "" | |||
| 161 | #bucket 默认是空,请填入唯一的bucket名称 | |||
| 162 | #例如test-bucket-20130101等带唯一日期的bucket名字. | |||
| 163 | ||||
| 164 | def check_not_empty(input, msg=""): | |||
| 165 | if not input: | |||
| 166 | print "Please make sure %s not empty!" % msg | |||
| 167 | exit(-1) | |||
| 168 | def check_res(res, msg=""): | |||
| 169 | if res.status / 100 == 2: | |||
| 170 | print "%s OK" % msg | |||
| 171 | else: | |||
| 172 | print "%s FAIL" % msg | |||
| 173 | print "ret:%s" % res.status | |||
| 174 | print "request-id:%s" % res.getheader("x-oss-request-id") | |||
| 175 | print "reason:%s" % res.read() | |||
| 176 | exit(-1) | |||
| 177 | ||||
| 178 | if __name__ == "__main__": | |||
| 179 | #初始化 | |||
| 180 | check_not_empty(ACCESS_ID, "ACCESS_ID") | |||
| 181 | check_not_empty(SECRET_ACCESS_KEY, "SECRET_ACCESS_KEY") | |||
| 182 | oss = OssAPI(HOST, ACCESS_ID, SECRET_ACCESS_KEY) | |||
| 183 | #创建属于自己的bucket | |||
| 184 | bucket = BUCKET | |||
| 185 | check_not_empty(bucket, "bucket") | |||
| 186 | acl = 'private' | |||
| 187 | headers = {} | |||
| 188 | res = oss.put_bucket(bucket, acl, headers) | |||
| 189 | check_res(res, "create bucket") | |||
| 190 | ||||
| 191 | #列出创建的bucket | |||
| 192 | res = oss.get_service() | |||
| 193 | check_res(res, "list all buckets") | |||
| 194 | #把指定的字符串内容上传到bucket中,在bucket中的文件名叫object。 | |||
| 195 | object = "object_test" | |||
| 196 | input_content = "hello, OSS" | |||
| 197 | content_type = "text/HTML" | |||
| 198 | headers = {} | |||
| 199 | res = oss.put_object_from_string(bucket, object, input_content, content_type, headers) | |||
| 200 | check_res(res, "upload from string") | |||
| 201 | #指定文件名, 把这个文件上传到bucket中,在bucket中的文件名叫object。 | |||
| 202 | object = "object_test" | |||
| 203 | filename = __file__ | |||
| 204 | content_type = "text/HTML" | |||
| 205 | headers = {} | |||
| 206 | res = oss.put_object_from_file(bucket, object, filename, content_type, headers) | |||
| 207 | check_res(res, "upload from localfile") | |||
| 208 | #下载bucket中的object,内容在body中 | |||
| 209 | object = "object_test" | |||
| 210 | headers = {} | |||
| 211 | res = oss.get_object(bucket, object, headers) | |||
| 212 | check_res(res, "download object") | |||
| 213 | #下载bucket中的object,把内容写入到本地文件中 | |||
| 214 | object = "object_test" | |||
| 215 | headers = {} | |||
| 216 | filename = "get_object_test_file" | |||
| 217 | res = oss.get_object_to_file(bucket, object, filename, headers) | |||
| 218 | if os.path.isfile(filename): | |||
| 219 | os.remove(filename) | |||
| 220 | check_res(res, "download object to localfile") | |||
| 221 | #查看bucket中所拥有的权限 | |||
| 222 | res = oss.get_bucket_acl(bucket) | |||
| 223 | check_res(res, "get bucket acl") | |||
| 224 | #列出bucket中所拥有的object | |||
| 225 | prefix = "" | |||
| 226 | marker = "" | |||
| 227 | delimiter = "/" | |||
| 228 | maxkeys = "100" | |||
| 229 | headers = {} | |||
| 230 | res = oss.get_bucket(bucket, prefix, marker, delimiter, maxkeys, headers) | |||
| 231 | check_res(res, "list objects in bucket") | |||
| 232 | #删除bucket中的object | |||
| 233 | object = "object_test" | |||
| 234 | headers = {} | |||
| 235 | res = oss.delete_object(bucket, object, headers) | |||
| 236 | check_res(res, "delete object") | |||
| 237 | #删除bucket | |||
| 238 | res = oss.delete_bucket(bucket) | |||
| 239 | check_res(res, "delete bucket") | |||
| 240 | ||||
| 241 | 11. 示例的预期结果: | |||
| 242 | create bucket OK | |||
| 243 | list all buckets OK | |||
| 244 | upload from string OK | |||
| 245 | upload from localfile OK | |||
| 246 | download object OK | |||
| 247 | download object to localfile OK | |||
| 248 | get bucket acl OK | |||
| 249 | list objects in bucket OK | |||
| 250 | delete object OK | |||
| 251 | delete bucket OK | |||
| 252 | ||||
| 253 | 使用osscmd | |||
| 254 | =============================================================== | |||
| 255 | 1. 在Linux上安装 osscmd. | |||
| 256 | sudo rm -r /usr/local/oss | |||
| 257 | sudo mkdir -p /usr/local/oss | |||
| 258 | sudo tar -zxf oss.tar.gz -C /usr/local/oss | |||
| 259 | sudo rm -f /usr/bin/osscmd | |||
| 260 | sudo ln -s /usr/local/oss/osscmd /usr/bin/osscmd | |||
| 261 | sudo chmod 755 /usr/local/oss/osscmd | |||
| 262 | ||||
| 263 | 在Windows上安装osscmd | |||
| 264 | 解压缩包 | |||
| 265 | ||||
| 266 | 2. 运行"python osscmd config --id=YOUR_ID --key=YOUR_KEY" | |||
| 267 | 用来配置访问OSS所需要的认证码 | |||
| 268 | ||||
| 269 | ||||
| 270 | 3. 运行"python osscmd getallbucket" 列出创建的bucket. | |||
| 271 | 如果是刚刚使用OSS的用户因为没有创建bucket,输出是空 | |||
| 272 | ||||
| 273 | 4. 运行"python osscmd createbucket mybucket" 创建一个名字 | |||
| 274 | 叫mybucket的bucket,有可能不成功,这个时候需要换一个名字。 | |||
| 275 | 因为OSS中的bucket名字是全局唯一的。 | |||
| 276 | ||||
| 277 | 5. 可以再次运行"python osscmd getallbucket" 查看是否创建成功。 | |||
| 278 | 如果没有成功请检查osscmd返回的错误信息。 | |||
| 279 | ||||
| 280 | 6. 成功创建bucket后。运行"python osscmd list oss://mybucket/", | |||
| 281 | 查看bucket中有哪些object。 | |||
| 282 | 由于bucket中还没有 | |||
| 283 | object,输出是空的。 | |||
| 284 | ||||
| 285 | 7. 向bucket中上传一个object. | |||
| 286 | $ md5sum my_local_file | |||
| 287 | 7625e1adc3a4b129763d580ca0a78e44 my_local_file | |||
| 288 | ||||
| 289 | $ python osscmd put my_local_file oss://mybucket/myobject | |||
| 290 | ||||
| 291 | 8. 如果创建成功,再次运行"python osscmd list oss://mybucket/" | |||
| 292 | ||||
| 293 | 9. 从bucket中下载object到本地文件,并比对下载的文件的md5值 | |||
| 294 | $ python osscmd get oss://mybucket/myobject local_file | |||
| 295 | ||||
| 296 | $ md5sum local_file | |||
| 297 | 7625e1adc3a4b129763d580ca0a78e44 local_file |
oss/oss_api.py
| File was created | 1 | #!/usr/bin/env python | ||
| 2 | #coding=utf-8 | |||
| 3 | ||||
| 4 | # Copyright (c) 2011, Alibaba Cloud Computing | |||
| 5 | # All rights reserved. | |||
| 6 | # | |||
| 7 | # Permission is hereby granted, free of charge, to any person obtaining a | |||
| 8 | # copy of this software and associated documentation files (the | |||
| 9 | # "Software"), to deal in the Software without restriction, including | |||
| 10 | # without limitation the rights to use, copy, modify, merge, publish, dis- | |||
| 11 | # tribute, sublicense, and/or sell copies of the Software, and to permit | |||
| 12 | # persons to whom the Software is furnished to do so, subject to the fol- | |||
| 13 | # lowing conditions: | |||
| 14 | # | |||
| 15 | # The above copyright notice and this permission notice shall be included | |||
| 16 | # in all copies or substantial portions of the Software. | |||
| 17 | # | |||
| 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |||
| 19 | # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- | |||
| 20 | # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT | |||
| 21 | # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |||
| 22 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
| 23 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |||
| 24 | # IN THE SOFTWARE. | |||
| 25 | ||||
| 26 | import httplib | |||
| 27 | import time | |||
| 28 | import base64 | |||
| 29 | import urllib | |||
| 30 | import StringIO | |||
| 31 | import sys | |||
| 32 | try: | |||
| 33 | from oss.oss_util import * | |||
| 34 | except: | |||
| 35 | from oss_util import * | |||
| 36 | try: | |||
| 37 | from oss.oss_xml_handler import * | |||
| 38 | except: | |||
| 39 | from oss_xml_handler import * | |||
| 40 | ||||
| 41 | class OssAPI: | |||
| 42 | ''' | |||
| 43 | A simple OSS API | |||
| 44 | ''' | |||
| 45 | DefaultContentType = 'application/octet-stream' | |||
| 46 | provider = PROVIDER | |||
| 47 | __version__ = '0.3.2' | |||
| 48 | Version = __version__ | |||
| 49 | AGENT = 'oss-python%s (%s)' % (__version__, sys.platform) | |||
| 50 | ||||
| 51 | def __init__(self, host, access_id, secret_access_key='', port=80, is_security=False): | |||
| 52 | self.SendBufferSize = 8192 | |||
| 53 | self.RecvBufferSize = 1024*1024*10 | |||
| 54 | self.host = get_second_level_domain(host) | |||
| 55 | self.port = port | |||
| 56 | self.access_id = access_id | |||
| 57 | self.secret_access_key = secret_access_key | |||
| 58 | self.show_bar = False | |||
| 59 | self.is_security = is_security | |||
| 60 | self.retry_times = 5 | |||
| 61 | self.agent = self.AGENT | |||
| 62 | self.debug = False | |||
| 63 | ||||
| 64 | def set_debug(self, is_debug): | |||
| 65 | if is_debug: | |||
| 66 | self.debug = True | |||
| 67 | ||||
| 68 | def set_retry_times(self, retry_times=5): | |||
| 69 | self.retry_times = retry_times | |||
| 70 | ||||
| 71 | def set_send_buf_size(self, buf_size): | |||
| 72 | try: | |||
| 73 | self.SendBufferSize = (int)(buf_size) | |||
| 74 | except ValueError: | |||
| 75 | pass | |||
| 76 | ||||
| 77 | def set_recv_buf_size(self, buf_size): | |||
| 78 | try: | |||
| 79 | self.RecvBufferSize = (int)(buf_size) | |||
| 80 | except ValueError: | |||
| 81 | pass | |||
| 82 | ||||
| 83 | def get_connection(self, tmp_host=None): | |||
| 84 | host = '' | |||
| 85 | port = 80 | |||
| 86 | timeout = 10 | |||
| 87 | if not tmp_host: | |||
| 88 | tmp_host = self.host | |||
| 89 | host_port_list = tmp_host.split(":") | |||
| 90 | if len(host_port_list) == 1: | |||
| 91 | host = host_port_list[0].strip() | |||
| 92 | elif len(host_port_list) == 2: | |||
| 93 | host = host_port_list[0].strip() | |||
| 94 | port = int(host_port_list[1].strip()) | |||
| 95 | if self.is_security or port == 443: | |||
| 96 | self.is_security = True | |||
| 97 | if sys.version_info >= (2, 6): | |||
| 98 | return httplib.HTTPSConnection(host=host, port=port, timeout=timeout) | |||
| 99 | else: | |||
| 100 | return httplib.HTTPSConnection(host=host, port=port) | |||
| 101 | else: | |||
| 102 | if sys.version_info >= (2, 6): | |||
| 103 | return httplib.HTTPConnection(host=host, port=port, timeout=timeout) | |||
| 104 | else: | |||
| 105 | return httplib.HTTPConnection(host=host, port=port) | |||
| 106 | ||||
| 107 | def sign_url_auth_with_expire_time(self, method, url, headers=None, resource="/", timeout=60, params=None): | |||
| 108 | ''' | |||
| 109 | Create the authorization for OSS based on the input method, url, body and headers | |||
| 110 | ||||
| 111 | :type method: string | |||
| 112 | :param method: one of PUT, GET, DELETE, HEAD | |||
| 113 | ||||
| 114 | :type url: string | |||
| 115 | :param:HTTP address of bucket or object, eg: http://HOST/bucket/object | |||
| 116 | ||||
| 117 | :type headers: dict | |||
| 118 | :param: HTTP header | |||
| 119 | ||||
| 120 | :type resource: string | |||
| 121 | :param:path of bucket or object, eg: /bucket/ or /bucket/object | |||
| 122 | ||||
| 123 | :type timeout: int | |||
| 124 | :param | |||
| 125 | ||||
| 126 | Returns: | |||
| 127 | signature url. | |||
| 128 | ''' | |||
| 129 | if not headers: | |||
| 130 | headers = {} | |||
| 131 | if not params: | |||
| 132 | params = {} | |||
| 133 | send_time = str(int(time.time()) + timeout) | |||
| 134 | headers['Date'] = send_time | |||
| 135 | auth_value = get_assign(self.secret_access_key, method, headers, resource, None, self.debug) | |||
| 136 | params["OSSAccessKeyId"] = self.access_id | |||
| 137 | params["Expires"] = str(send_time) | |||
| 138 | params["Signature"] = auth_value | |||
| 139 | sign_url = append_param(url, params) | |||
| 140 | return sign_url | |||
| 141 | ||||
| 142 | def sign_url(self, method, bucket, object, timeout=60, headers=None, params=None): | |||
| 143 | ''' | |||
| 144 | Create the authorization for OSS based on the input method, url, body and headers | |||
| 145 | ||||
| 146 | :type method: string | |||
| 147 | :param method: one of PUT, GET, DELETE, HEAD | |||
| 148 | ||||
| 149 | :type bucket: string | |||
| 150 | :param: | |||
| 151 | ||||
| 152 | :type object: string | |||
| 153 | :param: | |||
| 154 | ||||
| 155 | :type timeout: int | |||
| 156 | :param | |||
| 157 | ||||
| 158 | :type headers: dict | |||
| 159 | :param: HTTP header | |||
| 160 | ||||
| 161 | :type params: dict | |||
| 162 | :param: the parameters that put in the url address as query string | |||
| 163 | ||||
| 164 | :type resource: string | |||
| 165 | :param:path of bucket or object, eg: /bucket/ or /bucket/object | |||
| 166 | ||||
| 167 | Returns: | |||
| 168 | signature url. | |||
| 169 | ''' | |||
| 170 | if not headers: | |||
| 171 | headers = {} | |||
| 172 | if not params: | |||
| 173 | params = {} | |||
| 174 | send_time = str(int(time.time()) + timeout) | |||
| 175 | headers['Date'] = send_time | |||
| 176 | if isinstance(object, unicode): | |||
| 177 | object = object.encode('utf-8') | |||
| 178 | resource = "/%s/%s%s" % (bucket, object, get_resource(params)) | |||
| 179 | auth_value = get_assign(self.secret_access_key, method, headers, resource, None, self.debug) | |||
| 180 | params["OSSAccessKeyId"] = self.access_id | |||
| 181 | params["Expires"] = str(send_time) | |||
| 182 | params["Signature"] = auth_value | |||
| 183 | url = '' | |||
| 184 | if self.is_security: | |||
| 185 | if is_ip(self.host): | |||
| 186 | url = "https://%s/%s/%s" % (self.host, bucket, object) | |||
| 187 | else: | |||
| 188 | url = "https://%s.%s/%s" % (bucket, self.host, object) | |||
| 189 | else: | |||
| 190 | if is_ip(self.host): | |||
| 191 | url = "http://%s/%s/%s" % (self.host, bucket, object) | |||
| 192 | else: | |||
| 193 | url = "http://%s.%s/%s" % (bucket, self.host, object) | |||
| 194 | sign_url = append_param(url, params) | |||
| 195 | return sign_url | |||
| 196 | ||||
| 197 | def _create_sign_for_normal_auth(self, method, headers=None, resource="/"): | |||
| 198 | ''' | |||
| 199 | NOT public API | |||
| 200 | Create the authorization for OSS based on header input. | |||
| 201 | it should be put into "Authorization" parameter of header. | |||
| 202 | ||||
| 203 | :type method: string | |||
| 204 | :param:one of PUT, GET, DELETE, HEAD | |||
| 205 | ||||
| 206 | :type headers: dict | |||
| 207 | :param: HTTP header | |||
| 208 | ||||
| 209 | :type resource: string | |||
| 210 | :param:path of bucket or object, eg: /bucket/ or /bucket/object | |||
| 211 | ||||
| 212 | Returns: | |||
| 213 | signature string | |||
| 214 | ''' | |||
| 215 | auth_value = "%s %s:%s" % (self.provider, self.access_id, get_assign(self.secret_access_key, method, headers, resource, None, self.debug)) | |||
| 216 | return auth_value | |||
| 217 | ||||
| 218 | def bucket_operation(self, method, bucket, headers=None, params=None): | |||
| 219 | return self.http_request(method, bucket, '', headers, '', params) | |||
| 220 | ||||
| 221 | def object_operation(self, method, bucket, object, headers=None, body='', params=None): | |||
| 222 | return self.http_request(method, bucket, object, headers, body, params) | |||
| 223 | ||||
| 224 | def http_request(self, method, bucket, object, headers=None, body='', params=None): | |||
| 225 | ''' | |||
| 226 | Send http request of operation | |||
| 227 | ||||
| 228 | :type method: string | |||
| 229 | :param method: one of PUT, GET, DELETE, HEAD, POST | |||
| 230 | ||||
| 231 | :type bucket: string | |||
| 232 | :param | |||
| 233 | ||||
| 234 | :type object: string | |||
| 235 | :param | |||
| 236 | ||||
| 237 | :type headers: dict | |||
| 238 | :param: HTTP header | |||
| 239 | ||||
| 240 | :type body: string | |||
| 241 | :param | |||
| 242 | ||||
| 243 | Returns: | |||
| 244 | HTTP Response | |||
| 245 | ''' | |||
| 246 | retry = 5 | |||
| 247 | res = None | |||
| 248 | while retry > 0: | |||
| 249 | retry -= 1 | |||
| 250 | tmp_bucket = bucket | |||
| 251 | tmp_object = object | |||
| 252 | tmp_headers = {} | |||
| 253 | if headers and isinstance(headers, dict): | |||
| 254 | tmp_headers = headers.copy() | |||
| 255 | tmp_params = {} | |||
| 256 | if params and isinstance(params, dict): | |||
| 257 | tmp_params = params.copy() | |||
| 258 | ||||
| 259 | res = self.http_request_with_redirect(method, tmp_bucket, tmp_object, tmp_headers, body, tmp_params) | |||
| 260 | if res.status == 301 or res.status == 302: | |||
| 261 | self.host = helper_get_host_from_resp(res, bucket) | |||
| 262 | else: | |||
| 263 | return res | |||
| 264 | return res | |||
| 265 | ||||
| 266 | def http_request_with_redirect(self, method, bucket, object, headers=None, body='', params=None): | |||
| 267 | ''' | |||
| 268 | Send http request of operation | |||
| 269 | ||||
| 270 | :type method: string | |||
| 271 | :param method: one of PUT, GET, DELETE, HEAD, POST | |||
| 272 | ||||
| 273 | :type bucket: string | |||
| 274 | :param | |||
| 275 | ||||
| 276 | :type object: string | |||
| 277 | :param | |||
| 278 | ||||
| 279 | :type headers: dict | |||
| 280 | :param: HTTP header | |||
| 281 | ||||
| 282 | :type body: string | |||
| 283 | :param | |||
| 284 | ||||
| 285 | Returns: | |||
| 286 | HTTP Response | |||
| 287 | ''' | |||
| 288 | if not params: | |||
| 289 | params = {} | |||
| 290 | if not headers: | |||
| 291 | headers = {} | |||
| 292 | if isinstance(object, unicode): | |||
| 293 | object = object.encode('utf-8') | |||
| 294 | if not bucket: | |||
| 295 | resource = "/" | |||
| 296 | headers['Host'] = self.host | |||
| 297 | else: | |||
| 298 | headers['Host'] = "%s.%s" % (bucket, self.host) | |||
| 299 | resource = "/%s/" % bucket | |||
| 300 | resource = "%s%s%s" % (resource.encode('utf-8'), object, get_resource(params)) | |||
| 301 | object = urllib.quote(object) | |||
| 302 | url = "/%s" % object | |||
| 303 | if is_ip(self.host): | |||
| 304 | url = "/%s/%s" % (bucket, object) | |||
| 305 | if not bucket: | |||
| 306 | url = "/%s" % object | |||
| 307 | headers['Host'] = self.host | |||
| 308 | url = append_param(url, params) | |||
| 309 | date = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime()) | |||
| 310 | headers['Date'] = date | |||
| 311 | headers['Authorization'] = self._create_sign_for_normal_auth(method, headers, resource) | |||
| 312 | headers['User-Agent'] = self.agent | |||
| 313 | if check_bucket_valid(bucket) and not is_ip(self.host): | |||
| 314 | conn = self.get_connection(headers['Host']) | |||
| 315 | else: | |||
| 316 | conn = self.get_connection() | |||
| 317 | conn.request(method, url, body, headers) | |||
| 318 | return conn.getresponse() | |||
| 319 | ||||
| 320 | def get_service(self, headers=None): | |||
| 321 | ''' | |||
| 322 | List all buckets of user | |||
| 323 | ''' | |||
| 324 | return self.list_all_my_buckets(headers) | |||
| 325 | ||||
| 326 | def list_all_my_buckets(self, headers=None): | |||
| 327 | ''' | |||
| 328 | List all buckets of user | |||
| 329 | type headers: dict | |||
| 330 | :param | |||
| 331 | ||||
| 332 | Returns: | |||
| 333 | HTTP Response | |||
| 334 | ''' | |||
| 335 | method = 'GET' | |||
| 336 | bucket = '' | |||
| 337 | object = '' | |||
| 338 | body = '' | |||
| 339 | params = {} | |||
| 340 | return self.http_request(method, bucket, object, headers, body, params) | |||
| 341 | ||||
| 342 | def get_bucket_acl(self, bucket): | |||
| 343 | ''' | |||
| 344 | Get Access Control Level of bucket | |||
| 345 | ||||
| 346 | :type bucket: string | |||
| 347 | :param | |||
| 348 | ||||
| 349 | Returns: | |||
| 350 | HTTP Response | |||
| 351 | ''' | |||
| 352 | method = 'GET' | |||
| 353 | object = '' | |||
| 354 | headers = {} | |||
| 355 | body = '' | |||
| 356 | params = {} | |||
| 357 | params['acl'] = '' | |||
| 358 | return self.http_request(method, bucket, object, headers, body, params) | |||
| 359 | ||||
| 360 | def get_bucket_location(self, bucket): | |||
| 361 | ''' | |||
| 362 | Get Location of bucket | |||
| 363 | ''' | |||
| 364 | method = 'GET' | |||
| 365 | object = '' | |||
| 366 | headers = {} | |||
| 367 | body = '' | |||
| 368 | params = {} | |||
| 369 | params['location'] = '' | |||
| 370 | return self.http_request(method, bucket, object, headers, body, params) | |||
| 371 | ||||
| 372 | def get_bucket(self, bucket, prefix='', marker='', delimiter='', maxkeys='', headers=None): | |||
| 373 | ''' | |||
| 374 | List object that in bucket | |||
| 375 | ''' | |||
| 376 | return self.list_bucket(bucket, prefix, marker, delimiter, maxkeys, headers) | |||
| 377 | ||||
| 378 | def list_bucket(self, bucket, prefix='', marker='', delimiter='', maxkeys='', headers=None): | |||
| 379 | ''' | |||
| 380 | List object that in bucket | |||
| 381 | ||||
| 382 | :type bucket: string | |||
| 383 | :param | |||
| 384 | ||||
| 385 | :type prefix: string | |||
| 386 | :param | |||
| 387 | ||||
| 388 | :type marker: string | |||
| 389 | :param | |||
| 390 | ||||
| 391 | :type delimiter: string | |||
| 392 | :param | |||
| 393 | ||||
| 394 | :type maxkeys: string | |||
| 395 | :param | |||
| 396 | ||||
| 397 | :type headers: dict | |||
| 398 | :param: HTTP header | |||
| 399 | ||||
| 400 | Returns: | |||
| 401 | HTTP Response | |||
| 402 | ''' | |||
| 403 | method = 'GET' | |||
| 404 | object = '' | |||
| 405 | body = '' | |||
| 406 | params = {} | |||
| 407 | params['prefix'] = prefix | |||
| 408 | params['marker'] = marker | |||
| 409 | params['delimiter'] = delimiter | |||
| 410 | params['max-keys'] = maxkeys | |||
| 411 | return self.http_request(method, bucket, object, headers, body, params) | |||
| 412 | ||||
| 413 | def create_bucket(self, bucket, acl='', headers=None): | |||
| 414 | ''' | |||
| 415 | Create bucket | |||
| 416 | ''' | |||
| 417 | return self.put_bucket(bucket, acl, headers) | |||
| 418 | ||||
| 419 | def put_bucket(self, bucket, acl='', headers=None): | |||
| 420 | ''' | |||
| 421 | Create bucket | |||
| 422 | ||||
| 423 | :type bucket: string | |||
| 424 | :param | |||
| 425 | ||||
| 426 | :type acl: string | |||
| 427 | :param: one of private public-read public-read-write | |||
| 428 | ||||
| 429 | :type headers: dict | |||
| 430 | :param: HTTP header | |||
| 431 | ||||
| 432 | Returns: | |||
| 433 | HTTP Response | |||
| 434 | ''' | |||
| 435 | if not headers: | |||
| 436 | headers = {} | |||
| 437 | if acl != '': | |||
| 438 | if "AWS" == self.provider: | |||
| 439 | headers['x-amz-acl'] = acl | |||
| 440 | else: | |||
| 441 | headers['x-oss-acl'] = acl | |||
| 442 | method = 'PUT' | |||
| 443 | object = '' | |||
| 444 | body = '' | |||
| 445 | params = {} | |||
| 446 | return self.http_request(method, bucket, object, headers, body, params) | |||
| 447 | ||||
| 448 | def put_bucket_with_location(self, bucket, acl='', location='', headers=None): | |||
| 449 | ''' | |||
| 450 | Create bucket | |||
| 451 | ||||
| 452 | :type bucket: string | |||
| 453 | :param | |||
| 454 | ||||
| 455 | :type acl: string | |||
| 456 | :param: one of private public-read public-read-write | |||
| 457 | ||||
| 458 | :type location: string | |||
| 459 | :param: | |||
| 460 | ||||
| 461 | :type headers: dict | |||
| 462 | :param: HTTP header | |||
| 463 | ||||
| 464 | Returns: | |||
| 465 | HTTP Response | |||
| 466 | ''' | |||
| 467 | if not headers: | |||
| 468 | headers = {} | |||
| 469 | if acl != '': | |||
| 470 | if "AWS" == self.provider: | |||
| 471 | headers['x-amz-acl'] = acl | |||
| 472 | else: | |||
| 473 | headers['x-oss-acl'] = acl | |||
| 474 | params = {} | |||
| 475 | body = '' | |||
| 476 | if location != '': | |||
| 477 | body = r'<CreateBucketConfiguration>' | |||
| 478 | body += r'<LocationConstraint>' | |||
| 479 | body += location | |||
| 480 | body += r'</LocationConstraint>' | |||
| 481 | body += r'</CreateBucketConfiguration>' | |||
| 482 | method = 'PUT' | |||
| 483 | object = '' | |||
| 484 | return self.http_request(method, bucket, object, headers, body, params) | |||
| 485 | ||||
| 486 | def delete_bucket(self, bucket, headers=None): | |||
| 487 | ''' | |||
| 488 | Delete bucket | |||
| 489 | ||||
| 490 | :type bucket: string | |||
| 491 | :param | |||
| 492 | ||||
| 493 | Returns: | |||
| 494 | HTTP Response | |||
| 495 | ''' | |||
| 496 | method = 'DELETE' | |||
| 497 | object = '' | |||
| 498 | body = '' | |||
| 499 | params = {} | |||
| 500 | return self.http_request(method, bucket, object, headers, body, params) | |||
| 501 | ||||
| 502 | def put_object_with_data(self, bucket, object, input_content, content_type=DefaultContentType, headers=None, params=None): | |||
| 503 | ''' | |||
| 504 | Put object into bucket, the content of object is from input_content | |||
| 505 | ''' | |||
| 506 | return self.put_object_from_string(bucket, object, input_content, content_type, headers, params) | |||
| 507 | ||||
| 508 | def put_object_from_string(self, bucket, object, input_content, content_type=DefaultContentType, headers=None, params=None): | |||
| 509 | ''' | |||
| 510 | Put object into bucket, the content of object is from input_content | |||
| 511 | ||||
| 512 | :type bucket: string | |||
| 513 | :param | |||
| 514 | ||||
| 515 | :type object: string | |||
| 516 | :param | |||
| 517 | ||||
| 518 | :type input_content: string | |||
| 519 | :param | |||
| 520 | ||||
| 521 | :type content_type: string | |||
| 522 | :param: the object content type that supported by HTTP | |||
| 523 | ||||
| 524 | :type headers: dict | |||
| 525 | :param: HTTP header | |||
| 526 | ||||
| 527 | Returns: | |||
| 528 | HTTP Response | |||
| 529 | ''' | |||
| 530 | if not headers: | |||
| 531 | headers = {} | |||
| 532 | headers['Content-Type'] = content_type | |||
| 533 | headers['Content-Length'] = str(len(input_content)) | |||
| 534 | fp = StringIO.StringIO(input_content) | |||
| 535 | res = self.put_object_from_fp(bucket, object, fp, content_type, headers, params) | |||
| 536 | fp.close() | |||
| 537 | return res | |||
| 538 | ||||
| 539 | def _open_conn_to_put_object(self, bucket, object, filesize, content_type=DefaultContentType, headers=None, params=None): | |||
| 540 | ''' | |||
| 541 | NOT public API | |||
| 542 | Open a connectioon to put object | |||
| 543 | ||||
| 544 | :type bucket: string | |||
| 545 | :param | |||
| 546 | ||||
| 547 | :type filesize: int | |||
| 548 | :param | |||
| 549 | ||||
| 550 | :type object: string | |||
| 551 | :param | |||
| 552 | ||||
| 553 | :type input_content: string | |||
| 554 | :param | |||
| 555 | ||||
| 556 | :type content_type: string | |||
| 557 | :param: the object content type that supported by HTTP | |||
| 558 | ||||
| 559 | :type headers: dict | |||
| 560 | :param: HTTP header | |||
| 561 | ||||
| 562 | Returns: | |||
| 563 | Initialized HTTPConnection | |||
| 564 | ''' | |||
| 565 | if not params: | |||
| 566 | params = {} | |||
| 567 | if not headers: | |||
| 568 | headers = {} | |||
| 569 | method = 'PUT' | |||
| 570 | if isinstance(object, unicode): | |||
| 571 | object = object.encode('utf-8') | |||
| 572 | resource = "/%s/" % bucket | |||
| 573 | if not bucket: | |||
| 574 | resource = "/" | |||
| 575 | resource = "%s%s%s" % (resource.encode('utf-8'), object, get_resource(params)) | |||
| 576 | ||||
| 577 | object = urllib.quote(object) | |||
| 578 | url = "/%s" % object | |||
| 579 | if bucket: | |||
| 580 | headers['Host'] = "%s.%s" % (bucket, self.host) | |||
| 581 | else: | |||
| 582 | headers['Host'] = self.host | |||
| 583 | if is_ip(self.host): | |||
| 584 | url = "/%s/%s" % (bucket, object) | |||
| 585 | headers['Host'] = self.host | |||
| 586 | url = append_param(url, params) | |||
| 587 | date = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime()) | |||
| 588 | ||||
| 589 | if check_bucket_valid(bucket) and not is_ip(self.host): | |||
| 590 | conn = self.get_connection(headers['Host']) | |||
| 591 | else: | |||
| 592 | conn = self.get_connection() | |||
| 593 | conn.putrequest(method, url) | |||
| 594 | if isinstance(content_type, unicode): | |||
| 595 | content_type = content_type.encode('utf-8') | |||
| 596 | headers["Content-Type"] = content_type | |||
| 597 | headers["Content-Length"] = filesize | |||
| 598 | headers["Date"] = date | |||
| 599 | headers["Expect"] = "100-Continue" | |||
| 600 | headers['User-Agent'] = self.agent | |||
| 601 | for k in headers.keys(): | |||
| 602 | conn.putheader(str(k), str(headers[k])) | |||
| 603 | if '' != self.secret_access_key and '' != self.access_id: | |||
| 604 | auth = self._create_sign_for_normal_auth(method, headers, resource) | |||
| 605 | conn.putheader("Authorization", auth) | |||
| 606 | conn.endheaders() | |||
| 607 | return conn | |||
| 608 | ||||
| 609 | def put_object_from_file(self, bucket, object, filename, content_type='', headers=None, params=None): | |||
| 610 | ''' | |||
| 611 | put object into bucket, the content of object is read from file | |||
| 612 | ||||
| 613 | :type bucket: string | |||
| 614 | :param | |||
| 615 | ||||
| 616 | :type object: string | |||
| 617 | :param | |||
| 618 | ||||
| 619 | :type fllename: string | |||
| 620 | :param: the name of the read file | |||
| 621 | ||||
| 622 | :type content_type: string | |||
| 623 | :param: the object content type that supported by HTTP | |||
| 624 | ||||
| 625 | :type headers: dict | |||
| 626 | :param: HTTP header | |||
| 627 | ||||
| 628 | Returns: | |||
| 629 | HTTP Response | |||
| 630 | ''' | |||
| 631 | fp = open(filename, 'rb') | |||
| 632 | if not content_type: | |||
| 633 | content_type = get_content_type_by_filename(filename) | |||
| 634 | res = self.put_object_from_fp(bucket, object, fp, content_type, headers, params) | |||
| 635 | fp.close() | |||
| 636 | return res | |||
| 637 | ||||
| 638 | def view_bar(self, num=1, sum=100): | |||
| 639 | rate = float(num) / float(sum) | |||
| 640 | rate_num = int(rate * 100) | |||
| 641 | print '\r%d%% ' % (rate_num), | |||
| 642 | sys.stdout.flush() | |||
| 643 | ||||
| 644 | def put_object_from_fp(self, bucket, object, fp, content_type=DefaultContentType, headers=None, params=None): | |||
| 645 | ''' | |||
| 646 | Put object into bucket, the content of object is read from file pointer | |||
| 647 | ||||
| 648 | :type bucket: string | |||
| 649 | :param | |||
| 650 | ||||
| 651 | :type object: string | |||
| 652 | :param | |||
| 653 | ||||
| 654 | :type fp: file | |||
| 655 | :param: the pointer of the read file | |||
| 656 | ||||
| 657 | :type content_type: string | |||
| 658 | :param: the object content type that supported by HTTP | |||
| 659 | ||||
| 660 | :type headers: dict | |||
| 661 | :param: HTTP header | |||
| 662 | ||||
| 663 | Returns: | |||
| 664 | HTTP Response | |||
| 665 | ''' | |||
| 666 | tmp_object = object | |||
| 667 | tmp_headers = {} | |||
| 668 | tmp_params = {} | |||
| 669 | if headers and isinstance(headers, dict): | |||
| 670 | tmp_headers = headers.copy() | |||
| 671 | if params and isinstance(params, dict): | |||
| 672 | tmp_params = params.copy() | |||
| 673 | ||||
| 674 | fp.seek(os.SEEK_SET, os.SEEK_END) | |||
| 675 | filesize = fp.tell() | |||
| 676 | fp.seek(os.SEEK_SET) | |||
| 677 | conn = self._open_conn_to_put_object(bucket, object, filesize, content_type, headers, params) | |||
| 678 | totallen = 0 | |||
| 679 | l = fp.read(self.SendBufferSize) | |||
| 680 | retry_times = 0 | |||
| 681 | while len(l) > 0: | |||
| 682 | if retry_times > 100: | |||
| 683 | print "retry too many times" | |||
| 684 | raise | |||
| 685 | try: | |||
| 686 | conn.send(l) | |||
| 687 | retry_times = 0 | |||
| 688 | except: | |||
| 689 | retry_times += 1 | |||
| 690 | continue | |||
| 691 | totallen += len(l) | |||
| 692 | if self.show_bar: | |||
| 693 | self.view_bar(totallen, filesize) | |||
| 694 | l = fp.read(self.SendBufferSize) | |||
| 695 | res = conn.getresponse() | |||
| 696 | if res.status == 301 or res.status == 302: | |||
| 697 | self.host = helper_get_host_from_resp(res, bucket) | |||
| 698 | return self.put_object_from_fp(bucket, tmp_object, fp, content_type, tmp_headers, tmp_params) | |||
| 699 | return res | |||
| 700 | ||||
| 701 | def get_object(self, bucket, object, headers=None, params=None): | |||
| 702 | ''' | |||
| 703 | Get object | |||
| 704 | ||||
| 705 | :type bucket: string | |||
| 706 | :param | |||
| 707 | ||||
| 708 | :type object: string | |||
| 709 | :param | |||
| 710 | ||||
| 711 | :type headers: dict | |||
| 712 | :param: HTTP header | |||
| 713 | ||||
| 714 | Returns: | |||
| 715 | HTTP Response | |||
| 716 | ''' | |||
| 717 | method = 'GET' | |||
| 718 | body = '' | |||
| 719 | return self.http_request(method, bucket, object, headers, body, params) | |||
| 720 | ||||
| 721 | def get_object_to_file(self, bucket, object, filename, headers=None): | |||
| 722 | ''' | |||
| 723 | Get object and write the content of object into a file | |||
| 724 | ||||
| 725 | :type bucket: string | |||
| 726 | :param | |||
| 727 | ||||
| 728 | :type object: string | |||
| 729 | :param | |||
| 730 | ||||
| 731 | :type filename: string | |||
| 732 | :param | |||
| 733 | ||||
| 734 | :type headers: dict | |||
| 735 | :param: HTTP header | |||
| 736 | ||||
| 737 | Returns: | |||
| 738 | HTTP Response | |||
| 739 | ''' | |||
| 740 | res = self.get_object(bucket, object, headers) | |||
| 741 | totalread = 0 | |||
| 742 | if res.status / 100 == 2: | |||
| 743 | header = {} | |||
| 744 | header = convert_header2map(res.getheaders()) | |||
| 745 | filesize = safe_get_element("content-length", header) | |||
| 746 | f = file(filename, 'wb') | |||
| 747 | data = '' | |||
| 748 | while True: | |||
| 749 | data = res.read(self.RecvBufferSize) | |||
| 750 | if data: | |||
| 751 | f.write(data) | |||
| 752 | totalread += len(data) | |||
| 753 | if self.show_bar: | |||
| 754 | self.view_bar(totalread, filesize) | |||
| 755 | else: | |||
| 756 | break | |||
| 757 | f.close() | |||
| 758 | # TODO: get object with flow | |||
| 759 | return res | |||
| 760 | ||||
| 761 | def delete_object(self, bucket, object, headers=None): | |||
| 762 | ''' | |||
| 763 | Delete object | |||
| 764 | ||||
| 765 | :type bucket: string | |||
| 766 | :param | |||
| 767 | ||||
| 768 | :type object: string | |||
| 769 |