aliyun-sdk 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +364 -0
- data/lib/aliyun/oss.rb +17 -0
- data/lib/aliyun/oss/bucket.rb +555 -0
- data/lib/aliyun/oss/client.rb +100 -0
- data/lib/aliyun/oss/config.rb +34 -0
- data/lib/aliyun/oss/download.rb +216 -0
- data/lib/aliyun/oss/exception.rb +116 -0
- data/lib/aliyun/oss/http.rb +282 -0
- data/lib/aliyun/oss/iterator.rb +74 -0
- data/lib/aliyun/oss/logging.rb +43 -0
- data/lib/aliyun/oss/multipart.rb +60 -0
- data/lib/aliyun/oss/object.rb +15 -0
- data/lib/aliyun/oss/protocol.rb +1432 -0
- data/lib/aliyun/oss/struct.rb +199 -0
- data/lib/aliyun/oss/upload.rb +195 -0
- data/lib/aliyun/oss/util.rb +88 -0
- data/lib/aliyun/oss/version.rb +9 -0
- data/spec/aliyun/oss/bucket_spec.rb +595 -0
- data/spec/aliyun/oss/client/bucket_spec.rb +338 -0
- data/spec/aliyun/oss/client/client_spec.rb +228 -0
- data/spec/aliyun/oss/client/resumable_download_spec.rb +217 -0
- data/spec/aliyun/oss/client/resumable_upload_spec.rb +318 -0
- data/spec/aliyun/oss/http_spec.rb +26 -0
- data/spec/aliyun/oss/multipart_spec.rb +675 -0
- data/spec/aliyun/oss/object_spec.rb +741 -0
- data/spec/aliyun/oss/service_spec.rb +142 -0
- data/spec/aliyun/oss/util_spec.rb +50 -0
- metadata +181 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8d7df8217a8d711e910788b4478a63a9de6d3d19
|
4
|
+
data.tar.gz: 4a675680ae738c788a650425002973a1a90e27ac
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f82087353b70116909deaf0453bc4b65c213dfb37e8566d8a99f3ab1bc8e93dc6704c5c918400cfbe9c4799aa06e69d10eb45ab2d7e530e4f25864a6f189a92f
|
7
|
+
data.tar.gz: e61556df4633d1f99354d05078fbffd70c7ecbc13b44e3606553230d9faa4a969195a4f1aa2f4cd4a3602491ef8a89d6a17635c83e5a88e04f0ae9a8c63a0fb6
|
data/README.md
ADDED
@@ -0,0 +1,364 @@
|
|
1
|
+
# Aliyun OSS SDK for Ruby
|
2
|
+
|
3
|
+
![Build Status](http://cise.alibaba-inc.com/task/119062/build/10/status.svg)
|
4
|
+
|
5
|
+
-----
|
6
|
+
|
7
|
+
Aliyun::OSS是用于方便访问阿里云OSS(Object Storage Service)RESTful
|
8
|
+
API的Ruby客户端程序。了解OSS的的更多信息请访问OSS官网:
|
9
|
+
http://www.aliyun.com/product/oss
|
10
|
+
|
11
|
+
## 运行环境
|
12
|
+
|
13
|
+
- Ruby版本 >= 1.9.2
|
14
|
+
- 支持Ruby运行环境的Windows/Linux/OS X
|
15
|
+
|
16
|
+
安装Ruby请参考:https://www.ruby-lang.org/zh_cn/downloads/
|
17
|
+
|
18
|
+
## 快速开始
|
19
|
+
|
20
|
+
### 开通OSS账号
|
21
|
+
|
22
|
+
登录官网:http://www.aliyun.com/product/oss ,点击“立即开通”。按照提示
|
23
|
+
开通OSS服务。开通服务之后请在“管理控制台”中查看您的AccessKeyId和
|
24
|
+
AccessKeySecret,在使用Aliyun OSS SDK时需要提供您的这两个信息。
|
25
|
+
|
26
|
+
### 安装Aliyun OSS SDK for Ruby
|
27
|
+
|
28
|
+
gem install aliyun-sdk
|
29
|
+
|
30
|
+
并在你的程序中或者`irb`命令下包含:
|
31
|
+
|
32
|
+
require 'aliyun/oss'
|
33
|
+
|
34
|
+
**注意:**
|
35
|
+
|
36
|
+
1. SDK依赖的一些gem是本地扩展的形式,因此安装完Ruby之后还需要安装
|
37
|
+
ruby-dev以支持编译本地扩展的gem
|
38
|
+
2. SDK依赖的处理XML的gem(nokogiri)要求环境中包含zlib库
|
39
|
+
|
40
|
+
以Ubuntu为例,安装上述依赖的方法:
|
41
|
+
|
42
|
+
sudo apt-get install ruby-dev
|
43
|
+
sudo apt-get install zlib1g-dev
|
44
|
+
|
45
|
+
其他系统类似。
|
46
|
+
|
47
|
+
### 创建Client
|
48
|
+
|
49
|
+
client = Aliyun::OSS::Client.new(
|
50
|
+
:endpoint => endpoint,
|
51
|
+
:access_key_id => 'access_key_id',
|
52
|
+
:access_key_secret => 'access_key_secret')
|
53
|
+
|
54
|
+
其中`endpoint`是OSS服务的地址,根据节点区域不同,这个地址可能不一样,例如
|
55
|
+
杭州节点的地址是:`http://oss-cn-hangzhou.oss.aliyuncs.com`,其他节点的地址见:
|
56
|
+
[节点列表][1]
|
57
|
+
|
58
|
+
`access_key_id`和`access_key_secret`是您的服务凭证,在官网的“管理控制
|
59
|
+
台”上面可以查看。**请妥善保管您的AccessKeySecret,泄露之后可能影响您的
|
60
|
+
数据安全**
|
61
|
+
|
62
|
+
#### 使用用户绑定的域名作为endpoint
|
63
|
+
|
64
|
+
OSS支持自定义域名绑定,允许用户将自己的域名指向阿里云OSS的服务地址
|
65
|
+
(CNAME),这样用户迁移到OSS上时应用内资源的路径可以不用修改。绑定的域
|
66
|
+
名指向OSS的一个bucket。绑定域名的操作只能在OSS控制台进行。更多关于自定
|
67
|
+
义域名绑定的内容请到官网了解:[OSS自定义域名绑定][2]
|
68
|
+
|
69
|
+
用户绑定了域名后,使用SDK时指定的endpoint可以使用标准的OSS服务地址,也
|
70
|
+
可以使用用户绑定的域名:
|
71
|
+
|
72
|
+
client = Aliyun::OSS::Client.new(
|
73
|
+
:endpoint => 'http://img.my-domain.com',
|
74
|
+
:access_key_id => 'access_key_id',
|
75
|
+
:access_key_secret => 'access_key_secret',
|
76
|
+
:cname => true)
|
77
|
+
|
78
|
+
有以下几点需要注意:
|
79
|
+
|
80
|
+
1. 在Client初始化时必须指定:cname为true
|
81
|
+
2. 自定义域名绑定了OSS的一个bucket,所以用这种方式创建的client不能进行
|
82
|
+
list_buckets操作
|
83
|
+
3. 在{Aliyun::OSS::Client#get_bucket}时仍需要指定bucket名字,并且要与
|
84
|
+
域名所绑定的bucket名字相同
|
85
|
+
|
86
|
+
### 列出当前所有的Bucket
|
87
|
+
|
88
|
+
buckets = client.list_buckets
|
89
|
+
buckets.each{ |b| puts b.name }
|
90
|
+
|
91
|
+
`list_buckets`返回的是一个迭代器,用户依次获取每个Bucket的信息。Bucket
|
92
|
+
对象的结构请查看API文档中的{Aliyun::OSS::Bucket}
|
93
|
+
|
94
|
+
### 创建一个Bucket
|
95
|
+
|
96
|
+
bucket = client.create_bucket('my-bucket')
|
97
|
+
|
98
|
+
### 列出Bucket中所有的Object
|
99
|
+
|
100
|
+
bucket = client.get_bucket('my-bucket')
|
101
|
+
objects = bucket.list_objects
|
102
|
+
objects.each{ |o| puts o.key }
|
103
|
+
|
104
|
+
`list_objects`返回的是一个迭代器,用户依次获取每个Object的信息。Object
|
105
|
+
对象的结构请查看API文档中的{Aliyun::OSS::Object}
|
106
|
+
|
107
|
+
### 在Bucket中创建一个Object
|
108
|
+
|
109
|
+
bucket.put_object(object_key){ |stream| stream << 'hello world' }
|
110
|
+
|
111
|
+
用户也可以通过上传本地文件创建一个Object:
|
112
|
+
|
113
|
+
bucket.put_object(object_key, :file => local_file)
|
114
|
+
|
115
|
+
### 从Bucket中下载一个Object
|
116
|
+
|
117
|
+
bucket.get_object(object_key){ |content| puts content }
|
118
|
+
|
119
|
+
用户也可以将Object下载到本地文件中:
|
120
|
+
|
121
|
+
bucket.get_object(object_key, :file => local_file)
|
122
|
+
|
123
|
+
### 拷贝Object
|
124
|
+
|
125
|
+
bucket.copy_object(from_key, to_key)
|
126
|
+
|
127
|
+
### 判断一个Object是否存在
|
128
|
+
|
129
|
+
bucket.object_exists?(object_key)
|
130
|
+
|
131
|
+
更多Bucket的操作请参考API文档中的{Aliyun::OSS::Bucket}
|
132
|
+
|
133
|
+
## 模拟目录结构
|
134
|
+
|
135
|
+
OSS是Object存储服务,本身不支持目录结构,所有的object都是“平”的。但是
|
136
|
+
用户可以通过设置object的key为"/foo/bar/file"这样的形式来模拟目录结构。
|
137
|
+
假设现在有以下Objects:
|
138
|
+
|
139
|
+
/foo/x
|
140
|
+
/foo/bar/f1
|
141
|
+
/foo/bar/dir/file
|
142
|
+
/foo/hello/file
|
143
|
+
|
144
|
+
列出"/foo/"目录下的所有文件就是以"/foo/"为prefix进行`list_objects`,但
|
145
|
+
是这样也会把"/foo/bar/"下的所有object也列出来。为此需要用到delimiter参
|
146
|
+
数,其含义是从prefix往后遇到第一个delimiter时停止,这中间的key作为
|
147
|
+
Object的common prefix,包含在`list_objects`的结果中。
|
148
|
+
|
149
|
+
objs = bucket.list_objects(:prefix => '/foo/', :delimiter => '/')
|
150
|
+
objs.each do |i|
|
151
|
+
if i.is_a?(Object) # a object
|
152
|
+
puts "object: #{i.key}"
|
153
|
+
else
|
154
|
+
puts "common prefix: #{i}"
|
155
|
+
end
|
156
|
+
end
|
157
|
+
# output
|
158
|
+
object: /foo/x
|
159
|
+
common prefix: /foo/bar/
|
160
|
+
common prefix: /foo/hello/
|
161
|
+
|
162
|
+
Common prefix让用户不需要遍历所有的object(可能数量巨大)而找出前缀,
|
163
|
+
在模拟目录结构时非常有用。
|
164
|
+
|
165
|
+
## 断点上传/下载
|
166
|
+
|
167
|
+
OSS支持大文件的存储,用户如果上传/下载大文件(Object)的时候中断了(网络
|
168
|
+
闪断、程序崩溃、机器断电等),重新上传/下载是件很费资源的事情。OSS支持
|
169
|
+
Multipart的功能,可以在上传/下载时将大文件进行分片传输。Aliyun OSS SDK
|
170
|
+
基于此提供了断点上传/下载的功能。如果发生中断,可以从上次中断的地方继
|
171
|
+
续进行上传/下载。对于文件大小超过100MB的文件,都建议采用断点上传/下载
|
172
|
+
的方式进行。
|
173
|
+
|
174
|
+
### 断点上传
|
175
|
+
|
176
|
+
bucket.resumable_upload(object_key, local_file, :cpt_file => cpt_file)
|
177
|
+
|
178
|
+
其中`:cpt_file`指定保存上传中间状态的checkpoint文件所在的位置,如果用户
|
179
|
+
没有指定,SDK将为用户在`local_file`所在的目录生成一个
|
180
|
+
`local_file.cpt`。上传中断后,只需要提供相同的cpt文件,上传将会从
|
181
|
+
中断的点继续上传。所以典型的上传代码是:
|
182
|
+
|
183
|
+
retry_times = 5
|
184
|
+
retry_times.times do
|
185
|
+
begin
|
186
|
+
bucket.resumable_upload(object_key, local_file)
|
187
|
+
rescue => e
|
188
|
+
logger.error(e.message)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
注意:
|
193
|
+
|
194
|
+
1. SDK会将上传的中间状态信息记录在cpt文件中,所以要确保用户对cpt文
|
195
|
+
件有写权限
|
196
|
+
2. cpt文件记录了上传的中间状态信息并自带了校验,用户不能去编辑它,如
|
197
|
+
果cpt文件损坏则上传无法继续。整个上传完成后cpt文件会被删除。
|
198
|
+
|
199
|
+
### 断点下载
|
200
|
+
|
201
|
+
bucket.resumable_download(object_key, local_file, :cpt_file => cpt_file)
|
202
|
+
|
203
|
+
其中`:cpt_file`指定保存下载中间状态的checkpoint文件所在的位置,如果用户
|
204
|
+
没有指定,SDK将为用户在`local_file`所在的目录生成一个
|
205
|
+
`local_file.cpt`。下载中断后,只需要提供相同的cpt文件,下载将会从
|
206
|
+
中断的点继续下载。所以典型的下载代码是:
|
207
|
+
|
208
|
+
retry_times = 5
|
209
|
+
retry_times.times do
|
210
|
+
begin
|
211
|
+
bucket.resumable_download(object_key, local_file)
|
212
|
+
rescue => e
|
213
|
+
logger.error(e.message)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
注意:
|
218
|
+
|
219
|
+
1. 在下载过程中,对于下载完成的每个分片,会在`local_file`所在的目录生
|
220
|
+
成一个`local_file.part.N`的临时文件。整个下载完成后这些文件会被删除。
|
221
|
+
用户不能去编辑或删除part文件,否则下载不能继续。
|
222
|
+
2. SDK会将下载的中间状态信息记录在cpt文件中,所以要确保用户对cpt文
|
223
|
+
件有写权限
|
224
|
+
3. cpt文件记录了下载的中间状态信息并自带了校验,用户不能去编辑它,如
|
225
|
+
果cpt文件损坏则下载无法继续。整个下载完成后cpt文件会被删除。
|
226
|
+
|
227
|
+
|
228
|
+
## 可追加的文件
|
229
|
+
|
230
|
+
阿里云OSS中的Object分为两种类型:Normal和Appendable。
|
231
|
+
|
232
|
+
- 对于Normal Object,每次上传都是作为一个整体,如果一个Object已存在,
|
233
|
+
两次上传相同key的Object将会覆盖原有的Object
|
234
|
+
- 对于Appendable Object,第一次通过`append_object`创建它,后续的
|
235
|
+
`append_object`不会覆盖原有的内容,而是在Object末尾追加内容
|
236
|
+
- 不能向Normal Object追加内容
|
237
|
+
- 不能拷贝一个Appendable Object
|
238
|
+
|
239
|
+
### 创建一个Appendable Object
|
240
|
+
|
241
|
+
bucket.append_object(object_key, 0){ |stream| stream << "hello world" }
|
242
|
+
|
243
|
+
第二个参数是追加的位置,对一个Object第一次追加时,这个参数为0。后续的
|
244
|
+
追加这个参数要求是追加前Object的长度。
|
245
|
+
|
246
|
+
当然,也可以从文件中读取追加的内容:
|
247
|
+
|
248
|
+
bucket.append_object(object_key, 0, :file => local_file)
|
249
|
+
|
250
|
+
### 向Object追加内容
|
251
|
+
|
252
|
+
pos = bucket.get_object_meta(object_key).size
|
253
|
+
next_pos = bucket.append_object(object_key, pos, :file => local_file)
|
254
|
+
|
255
|
+
程序第一次追加时,可以通过{Aliyun::OSS::Bucket#get_object_meta}获取文件的长度,
|
256
|
+
后续追加时,可以根据{Aliyun::OSS::Bucket#append_object}返回的下次追加长度。
|
257
|
+
|
258
|
+
注意:如果并发地`append_object`,`next_pos`并不总是对的。
|
259
|
+
|
260
|
+
## Object meta信息
|
261
|
+
|
262
|
+
在上传Object时,除了Object内容,OSS还允许用户为Object设置一些"meta信息
|
263
|
+
",这些meta信息是一个个的Key-Value对,用于标识Object特有的属性信息。这
|
264
|
+
些meta信息会跟Object一起存储,并在`get_object`和`get_object_meta`时返
|
265
|
+
回给用户。
|
266
|
+
|
267
|
+
bucket.put_object(object_key, :file => local_file,
|
268
|
+
:metas => {
|
269
|
+
'key1' => 'value1',
|
270
|
+
'key2' => 'value2'})
|
271
|
+
|
272
|
+
obj = bucket.get_object(object_key, :file => localfile)
|
273
|
+
puts obj.metas
|
274
|
+
|
275
|
+
关于meta信息有以下几点需要注意:
|
276
|
+
1. meta信息的key和value都只能是简单的ASCII非换行字符,并且总的大小不能超过8KB。
|
277
|
+
2. Copy object时默认将拷贝源object的meta信息,如果用户不希望这么做,需要
|
278
|
+
显式地将`:meta_directive`设置成{Aliyun::OSS::MetaDirective::REPLACE}
|
279
|
+
|
280
|
+
## 权限控制
|
281
|
+
|
282
|
+
OSS允许用户对Bucket和Object分别设置访问权限,方便用户控制自己的资源可
|
283
|
+
以被如何访问。对于Bucket,有三种访问权限:
|
284
|
+
|
285
|
+
- public-read-write 允许匿名用户向该Bucket中创建/获取/删除Object
|
286
|
+
- public-read 允许匿名用户获取该Bucket中的Object
|
287
|
+
- private 不允许匿名访问,所有的访问都要经过签名
|
288
|
+
|
289
|
+
创建Bucket时,默认是private权限。之后用户可以通过`bucket.acl=`来设置
|
290
|
+
Bucket的权限。
|
291
|
+
|
292
|
+
bucket.acl = Aliyun::OSS::ACL::PUBLIC_READ
|
293
|
+
puts bucket.acl # public-read
|
294
|
+
|
295
|
+
对于Object,有四种访问权限:
|
296
|
+
|
297
|
+
- default 继承所属的Bucket的访问权限,即与所属Bucket的权限值一样
|
298
|
+
- public-read-write 允许匿名用户读写该Object
|
299
|
+
- public-read 允许匿名用户读该Object
|
300
|
+
- private 不允许匿名访问,所有的访问都要经过签名
|
301
|
+
|
302
|
+
创建Object时,默认为default权限。之后用户可以通过
|
303
|
+
`bucket.set_object_acl`来设置Object的权限。
|
304
|
+
|
305
|
+
acl = bucket.get_object_acl(object_key)
|
306
|
+
puts acl # default
|
307
|
+
bucket.set_object_acl(object_key, Aliyun::OSS::ACL::PUBLIC_READ)
|
308
|
+
acl = bucket.get_object_acl(object_key)
|
309
|
+
puts acl # public-read
|
310
|
+
|
311
|
+
需要注意的是:
|
312
|
+
|
313
|
+
1. 如果设置了Object的权限,则访问该Object时进行权限认证时会优先判断
|
314
|
+
Object的权限,而Bucket的权限设置会被忽略。
|
315
|
+
2. 允许匿名访问时(设置了public-read或者public-read-write权限),用户
|
316
|
+
可以直接通过浏览器访问,例如:
|
317
|
+
|
318
|
+
http://bucket-name.oss-cn-hangzhou.aliyuncs.com/object.jpg
|
319
|
+
|
320
|
+
3. 访问具有public权限的Bucket/Object时,也可以通过创建匿名的Client来进行:
|
321
|
+
|
322
|
+
# 不填access_key_id和access_key_secret,将创建匿名Client,只能访问
|
323
|
+
# 具有public权限的Bucket/Object
|
324
|
+
client = Client.new(:endpoint => 'oss-cn-hangzhou.aliyuncs.com')
|
325
|
+
bucket = client.get_bucket('public-bucket')
|
326
|
+
obj = bucket.get_object('public-object', :file => local_file)
|
327
|
+
|
328
|
+
## 运行examples
|
329
|
+
|
330
|
+
SDK的examples/目录下有一些展示SDK功能的示例程序,用户稍加配置就可以直
|
331
|
+
接运行。examples需要的权限信息和bucket信息从用户`HOME`目录下的配置文件
|
332
|
+
`~/.oss.yml`中读取,其中应该包含以下字段:
|
333
|
+
|
334
|
+
endpoint: oss-cn-hangzhou.aliyuncs.com
|
335
|
+
cname: false
|
336
|
+
id: ACCESS KEY ID
|
337
|
+
key: ACCESS KEY SECRET
|
338
|
+
bucket: BUCKET NAME
|
339
|
+
|
340
|
+
用户需要创建(如果不存在)或者修改其中的内容,然后运行:
|
341
|
+
|
342
|
+
ruby examples/aliyun/oss/bucket.rb
|
343
|
+
|
344
|
+
## 运行测试
|
345
|
+
|
346
|
+
SDK采用rspec进行测试,如果要对SDK进行修改,请确保没有break现有测试。测
|
347
|
+
试运行的方法是,在ruby-sdk/目录下运行:
|
348
|
+
|
349
|
+
rspec
|
350
|
+
|
351
|
+
或者用bundle和rake:
|
352
|
+
|
353
|
+
bundle exec rake spec
|
354
|
+
|
355
|
+
## 更多
|
356
|
+
|
357
|
+
更多文档请查看:
|
358
|
+
|
359
|
+
- 阿里云官网文档:http://help.aliyun.com/product/8314910_oss.html
|
360
|
+
|
361
|
+
|
362
|
+
[1]: http://help.aliyun.com/document_detail/oss/product-documentation/domain-region.html
|
363
|
+
|
364
|
+
[2]: http://help.aliyun.com/document_detail/oss/product-documentation/function/cname.html
|
data/lib/aliyun/oss.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require_relative 'oss/version'
|
4
|
+
require_relative 'oss/logging'
|
5
|
+
require_relative 'oss/util'
|
6
|
+
require_relative 'oss/exception'
|
7
|
+
require_relative 'oss/struct'
|
8
|
+
require_relative 'oss/config'
|
9
|
+
require_relative 'oss/http'
|
10
|
+
require_relative 'oss/protocol'
|
11
|
+
require_relative 'oss/multipart'
|
12
|
+
require_relative 'oss/upload'
|
13
|
+
require_relative 'oss/download'
|
14
|
+
require_relative 'oss/iterator'
|
15
|
+
require_relative 'oss/object'
|
16
|
+
require_relative 'oss/bucket'
|
17
|
+
require_relative 'oss/client'
|
@@ -0,0 +1,555 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module Aliyun
|
4
|
+
module OSS
|
5
|
+
##
|
6
|
+
# Bucket是用户的Object相关的操作的client,主要包括三部分功能:
|
7
|
+
# 1. bucket相关:获取/设置bucket的属性(acl, logging, referer,
|
8
|
+
# website, lifecycle, cors)
|
9
|
+
# 2. object相关:上传、下载、追加、拷贝object等
|
10
|
+
# 3. multipart相关:断点续传、断点续载
|
11
|
+
class Bucket < Struct::Base
|
12
|
+
|
13
|
+
attrs :name, :location, :creation_time
|
14
|
+
|
15
|
+
def initialize(opts = {}, protocol = nil)
|
16
|
+
super(opts)
|
17
|
+
@protocol = protocol
|
18
|
+
end
|
19
|
+
|
20
|
+
### Bucket相关的API ###
|
21
|
+
|
22
|
+
# 获取Bucket的ACL
|
23
|
+
# @return [String] Bucket的{OSS::ACL ACL}
|
24
|
+
def acl
|
25
|
+
@protocol.get_bucket_acl(name)
|
26
|
+
end
|
27
|
+
|
28
|
+
# 设置Bucket的ACL
|
29
|
+
# @param acl [String] Bucket的{OSS::ACL ACL}
|
30
|
+
def acl=(acl)
|
31
|
+
@protocol.put_bucket_acl(name, acl)
|
32
|
+
end
|
33
|
+
|
34
|
+
# 获取Bucket的logging配置
|
35
|
+
# @return [BucketLogging] Bucket的logging配置
|
36
|
+
def logging
|
37
|
+
@protocol.get_bucket_logging(name)
|
38
|
+
end
|
39
|
+
|
40
|
+
# 设置Bucket的logging配置
|
41
|
+
# @param logging [BucketLogging] logging配置
|
42
|
+
def logging=(logging)
|
43
|
+
if logging.enabled?
|
44
|
+
@protocol.put_bucket_logging(name, logging)
|
45
|
+
else
|
46
|
+
@protocol.delete_bucket_logging(name)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# 获取Bucket的website配置
|
51
|
+
# @return [BucketWebsite] Bucket的website配置
|
52
|
+
def website
|
53
|
+
begin
|
54
|
+
w = @protocol.get_bucket_website(name)
|
55
|
+
rescue ServerError => e
|
56
|
+
raise unless e.http_code == 404
|
57
|
+
end
|
58
|
+
|
59
|
+
w || BucketWebsite.new
|
60
|
+
end
|
61
|
+
|
62
|
+
# 设置Bucket的website配置
|
63
|
+
# @param website [BucketWebsite] website配置
|
64
|
+
def website=(website)
|
65
|
+
if website.enabled?
|
66
|
+
@protocol.put_bucket_website(name, website)
|
67
|
+
else
|
68
|
+
@protocol.delete_bucket_website(name)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# 获取Bucket的Referer配置
|
73
|
+
# @return [BucketReferer] Bucket的Referer配置
|
74
|
+
def referer
|
75
|
+
@protocol.get_bucket_referer(name)
|
76
|
+
end
|
77
|
+
|
78
|
+
# 设置Bucket的Referer配置
|
79
|
+
# @param referer [BucketReferer] Referer配置
|
80
|
+
def referer=(referer)
|
81
|
+
@protocol.put_bucket_referer(name, referer)
|
82
|
+
end
|
83
|
+
|
84
|
+
# 获取Bucket的生命周期配置
|
85
|
+
# @return [Array<OSS::LifeCycleRule>] Bucket的生命周期规则,如果
|
86
|
+
# 当前Bucket未设置lifecycle,则返回[]
|
87
|
+
def lifecycle
|
88
|
+
begin
|
89
|
+
r = @protocol.get_bucket_lifecycle(name)
|
90
|
+
rescue ServerError => e
|
91
|
+
raise unless e.http_code == 404
|
92
|
+
end
|
93
|
+
|
94
|
+
r || []
|
95
|
+
end
|
96
|
+
|
97
|
+
# 设置Bucket的生命周期配置
|
98
|
+
# @param rules [Array<OSS::LifeCycleRule>] 生命
|
99
|
+
# 周期配置规则
|
100
|
+
# @see OSS::LifeCycleRule 查看如何设置生命周期规则
|
101
|
+
# @note 如果rules为空,则会删除这个bucket上的lifecycle配置
|
102
|
+
def lifecycle=(rules)
|
103
|
+
if rules.empty?
|
104
|
+
@protocol.delete_bucket_lifecycle(name)
|
105
|
+
else
|
106
|
+
@protocol.put_bucket_lifecycle(name, rules)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# 获取Bucket的跨域资源共享(CORS)的规则
|
111
|
+
# @return [Array<OSS::CORSRule>] Bucket的CORS规则,如果当前
|
112
|
+
# Bucket未设置CORS规则,则返回[]
|
113
|
+
def cors
|
114
|
+
begin
|
115
|
+
r = @protocol.get_bucket_cors(name)
|
116
|
+
rescue ServerError => e
|
117
|
+
raise unless e.http_code == 404
|
118
|
+
end
|
119
|
+
|
120
|
+
r || []
|
121
|
+
end
|
122
|
+
|
123
|
+
# 设置Bucket的跨域资源共享(CORS)的规则
|
124
|
+
# @param rules [Array<OSS::CORSRule>] CORS规则
|
125
|
+
# @note 如果rules为空,则会删除这个bucket上的CORS配置
|
126
|
+
def cors=(rules)
|
127
|
+
if rules.empty?
|
128
|
+
@protocol.delete_bucket_cors(name)
|
129
|
+
else
|
130
|
+
@protocol.set_bucket_cors(name, rules)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
### Object相关的API ###
|
135
|
+
|
136
|
+
|
137
|
+
# 列出bucket中的object
|
138
|
+
# @param opts [Hash] 查询选项
|
139
|
+
# @option opts [String] :prefix 返回的object的前缀,如果设置则只
|
140
|
+
# 返回那些名字以它为前缀的object
|
141
|
+
# @option opts [String] :delimiter 用于获取公共前缀的分隔符,从
|
142
|
+
# 前缀后面开始到第一个分隔符出现的位置之前的字符,作为公共前缀。
|
143
|
+
# @example
|
144
|
+
# 假设我们有如下objects:
|
145
|
+
# /foo/bar/obj1
|
146
|
+
# /foo/bar/obj2
|
147
|
+
# ...
|
148
|
+
# /foo/bar/obj9999999
|
149
|
+
# /foo/xxx/
|
150
|
+
# 用'foo/'作为前缀, '/'作为分隔符, 则得到的公共前缀是:
|
151
|
+
# '/foo/bar/', '/foo/xxx/'。它们恰好就是目录'/foo/'下的所有子目
|
152
|
+
# 录。用delimiter获取公共前缀的方法避免了查询当前bucket下的所有
|
153
|
+
# object(可能数量巨大),是用于模拟目录结构的常用做法。
|
154
|
+
# @option opts [String] :encoding 指定返回的响应中object名字的编
|
155
|
+
# 码方法,目前只支持{OSS::KeyEncoding::URL}编码方式。
|
156
|
+
# @return [Enumerator<Object>] 其中Object可能是{OSS::Object},也
|
157
|
+
# 可能是{String},此时它是一个公共前缀
|
158
|
+
# @example
|
159
|
+
# all = bucket.list_objects
|
160
|
+
# all.each do |i|
|
161
|
+
# if i.is_a?(Object)
|
162
|
+
# puts "Object: #{i.key}"
|
163
|
+
# else
|
164
|
+
# puts "Common prefix: #{i}"
|
165
|
+
# end
|
166
|
+
# end
|
167
|
+
def list_objects(opts = {})
|
168
|
+
Iterator::Objects.new(@protocol, name, opts).to_enum
|
169
|
+
end
|
170
|
+
|
171
|
+
# 向Bucket中上传一个object
|
172
|
+
# @param key [String] Object的名字
|
173
|
+
# @param opts [Hash] 上传object时的选项(可选)
|
174
|
+
# @option opts [String] :file 设置所上传的文件
|
175
|
+
# @option opts [String] :content_type 设置所上传的内容的
|
176
|
+
# Content-Type,默认是application/octet-stream
|
177
|
+
# @option opts [Hash] :metas 设置object的meta,这是一些用户自定
|
178
|
+
# 义的属性,它们会和object一起存储,在{#get_object_meta}的时候会
|
179
|
+
# 返回这些meta。属性的key不区分大小写。例如:{ 'year' => '2015' }
|
180
|
+
# @yield [HTTP::StreamWriter] 如果调
|
181
|
+
# 用的时候传递了block,则写入到object的数据由block指定
|
182
|
+
# @example 流式上传数据
|
183
|
+
# put_object('x'){ |stream| 100.times { |i| stream << i.to_s } }
|
184
|
+
# put_object('x'){ |stream| stream << get_data }
|
185
|
+
# @example 上传文件
|
186
|
+
# put_object('x', :file => '/tmp/x')
|
187
|
+
# @example 指定Content-Type和metas
|
188
|
+
# put_object('x', :file => '/tmp/x', :content_type => 'text/html',
|
189
|
+
# :metas => {'year' => '2015', 'people' => 'mary'})
|
190
|
+
# @note 采用streaming的方式时,提供的数据必须是有结束标记的数据。
|
191
|
+
# 因为put_object会不断地从StreamWriter中读取数据上传到OSS,直到
|
192
|
+
# 它读到的数据为nil停止。
|
193
|
+
# @note 如果opts中指定了:file,则block会被忽略
|
194
|
+
def put_object(key, opts = {}, &block)
|
195
|
+
file = opts[:file]
|
196
|
+
if file
|
197
|
+
opts[:content_type] = get_content_type(file)
|
198
|
+
|
199
|
+
@protocol.put_object(name, key, opts) do |sw|
|
200
|
+
File.open(File.expand_path(file), 'rb') do |f|
|
201
|
+
sw << f.read(Protocol::STREAM_CHUNK_SIZE) until f.eof?
|
202
|
+
end
|
203
|
+
end
|
204
|
+
else
|
205
|
+
@protocol.put_object(name, key, opts, &block)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# 从Bucket中下载一个object
|
210
|
+
# @param key [String] Object的名字
|
211
|
+
# @param opts [Hash] 下载Object的选项(可选)
|
212
|
+
# @option opts [Array<Integer>] :range 指定下载object的部分数据,
|
213
|
+
# range应只包含两个数字,表示一个*左开右闭*的bytes range
|
214
|
+
# @option opts [String] :file 指定将下载的object写入到文件中
|
215
|
+
# @option opts [Hash] :condition 指定下载object需要满足的条件
|
216
|
+
# * :if_modified_since (Time) 指定如果object的修改时间晚于这个值,则下载
|
217
|
+
# * :if_unmodified_since (Time) 指定如果object从这个时间后再无修改,则下载
|
218
|
+
# * :if_match_etag (String) 指定如果object的etag等于这个值,则下载
|
219
|
+
# * :if_unmatch_etag (String) 指定如果object的etag不等于这个值,则下载
|
220
|
+
# @option opts [Hash] :rewrite 指定下载object时Server端返回的响应头部字段的值
|
221
|
+
# * :content_type (String) 指定返回的响应中Content-Type的值
|
222
|
+
# * :content_language (String) 指定返回的响应中Content-Language的值
|
223
|
+
# * :expires (Time) 指定返回的响应中Expires的值
|
224
|
+
# * :cache_control (String) 指定返回的响应中Cache-Control的值
|
225
|
+
# * :content_disposition (String) 指定返回的响应中Content-Disposition的值
|
226
|
+
# * :content_encoding (String) 指定返回的响应中Content-Encoding的值
|
227
|
+
# @return [OSS::Object] 返回Object对象
|
228
|
+
# @yield [String] 如果调用的时候传递了block,则获取到的object的数据交由block处理
|
229
|
+
# @example 流式下载文件
|
230
|
+
# get_object('x'){ |chunk| handle_chunk_data(chunk) }
|
231
|
+
# @example 下载到本地文件
|
232
|
+
# get_object('x', :file => '/tmp/x')
|
233
|
+
# @example 指定检查条件
|
234
|
+
# get_object('x', :file => '/tmp/x', :condition => {:if_match_etag => 'etag'})
|
235
|
+
# @example 指定重写响应的header信息
|
236
|
+
# get_object('x', :file => '/tmp/x', :rewrite => {:content_type => 'text/html'})
|
237
|
+
# @note 如果opts中指定了`:file`,则block会被忽略
|
238
|
+
# @note 如果既没有指定`:file`也没有指定block,则只获取Object
|
239
|
+
# meta而不下载Object内容
|
240
|
+
def get_object(key, opts = {}, &block)
|
241
|
+
obj = nil
|
242
|
+
file = opts[:file]
|
243
|
+
if file
|
244
|
+
File.open(File.expand_path(file), 'wb') do |f|
|
245
|
+
obj = @protocol.get_object(name, key, opts) do |chunk|
|
246
|
+
f.write(chunk)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
elsif block
|
250
|
+
obj = @protocol.get_object(name, key, opts, &block)
|
251
|
+
else
|
252
|
+
obj = @protocol.get_object_meta(name, key, opts)
|
253
|
+
end
|
254
|
+
|
255
|
+
obj
|
256
|
+
end
|
257
|
+
|
258
|
+
# 更新Object的metas
|
259
|
+
# @param key [String] Object的名字
|
260
|
+
# @param metas [Hash] Object的meta
|
261
|
+
# @param conditions [Hash] 指定更新Object meta需要满足的条件,
|
262
|
+
# 同{#get_object}
|
263
|
+
# @return [Hash] 更新后文件的信息
|
264
|
+
# * :etag [String] 更新后文件的ETag
|
265
|
+
# * :last_modified [Time] 更新后文件的最后修改时间
|
266
|
+
def update_object_metas(key, metas, conditions = {})
|
267
|
+
@protocol.copy_object(
|
268
|
+
name, key, key,
|
269
|
+
:meta_directive => MetaDirective::REPLACE,
|
270
|
+
:metas => metas,
|
271
|
+
:condition => conditions)
|
272
|
+
end
|
273
|
+
|
274
|
+
# 判断一个object是否存在
|
275
|
+
# @param key [String] Object的名字
|
276
|
+
# @return [Boolean] 如果Object存在返回true,否则返回false
|
277
|
+
def object_exists?(key)
|
278
|
+
begin
|
279
|
+
get_object(key)
|
280
|
+
return true
|
281
|
+
rescue ServerError => e
|
282
|
+
return false if e.http_code == 404
|
283
|
+
raise e
|
284
|
+
end
|
285
|
+
|
286
|
+
false
|
287
|
+
end
|
288
|
+
|
289
|
+
alias :object_exist? :object_exists?
|
290
|
+
|
291
|
+
# 向Bucket中的object追加内容。如果object不存在,则创建一个
|
292
|
+
# Appendable Object。
|
293
|
+
# @param key [String] Object的名字
|
294
|
+
# @param opts [Hash] 上传object时的选项(可选)
|
295
|
+
# @option opts [String] :file 指定追加的内容从文件中读取
|
296
|
+
# @option opts [String] :content_type 设置所上传的内容的
|
297
|
+
# Content-Type,默认是application/octet-stream
|
298
|
+
# @option opts [Hash] :metas 设置object的meta,这是一些用户自定
|
299
|
+
# 义的属性,它们会和object一起存储,在{#get_object_meta}的时候会
|
300
|
+
# 返回这些meta。属性的key不区分大小写。例如:{ 'year' => '2015' }
|
301
|
+
# @example 流式上传数据
|
302
|
+
# pos = append_object('x', 0){ |stream| 100.times { |i| stream << i.to_s } }
|
303
|
+
# append_object('x', pos){ |stream| stream << get_data }
|
304
|
+
# @example 上传文件
|
305
|
+
# append_object('x', 0, :file => '/tmp/x')
|
306
|
+
# @example 指定Content-Type和metas
|
307
|
+
# append_object('x', 0, :file => '/tmp/x', :content_type => 'text/html',
|
308
|
+
# :metas => {'year' => '2015', 'people' => 'mary'})
|
309
|
+
# @return [Integer] 返回下次append的位置
|
310
|
+
# @yield [HTTP::StreamWriter] 同 {#put_object}
|
311
|
+
def append_object(key, pos, opts = {}, &block)
|
312
|
+
next_pos = -1
|
313
|
+
file = opts[:file]
|
314
|
+
if file
|
315
|
+
opts[:content_type] = get_content_type(file)
|
316
|
+
|
317
|
+
next_pos = @protocol.append_object(name, key, pos, opts) do |sw|
|
318
|
+
File.open(File.expand_path(file), 'rb') do |f|
|
319
|
+
sw << f.read(Protocol::STREAM_CHUNK_SIZE) until f.eof?
|
320
|
+
end
|
321
|
+
end
|
322
|
+
else
|
323
|
+
next_pos = @protocol.append_object(name, key, pos, opts, &block)
|
324
|
+
end
|
325
|
+
|
326
|
+
next_pos
|
327
|
+
end
|
328
|
+
|
329
|
+
# 将Bucket中的一个object拷贝成另外一个object
|
330
|
+
# @param source [String] 源object名字
|
331
|
+
# @param dest [String] 目标object名字
|
332
|
+
# @param opts [Hash] 拷贝object时的选项(可选)
|
333
|
+
# @option opts [String] :acl 目标文件的acl属性,默认为private
|
334
|
+
# @option opts [String] :meta_directive 指定是否拷贝源object的
|
335
|
+
# meta信息,默认为{OSS::MetaDirective::COPY}:即拷贝object的时
|
336
|
+
# 候也拷贝meta信息。
|
337
|
+
# @option opts [Hash] :metas 设置object的meta,这是一些用户自定
|
338
|
+
# 义的属性,它们会和object一起存储,在{#get_object_meta}的时候会
|
339
|
+
# 返回这些meta。属性的key不区分大小写。例如:{ 'year' => '2015'
|
340
|
+
# }。如果:meta_directive为{OSS::MetaDirective::COPY},则:metas
|
341
|
+
# 会被忽略。
|
342
|
+
# @option opts [Hash] :condition 指定拷贝object需要满足的条件,
|
343
|
+
# 同 {#get_object}
|
344
|
+
# @return [Hash] 目标文件的信息
|
345
|
+
# * :etag [String] 目标文件的ETag
|
346
|
+
# * :last_modified [Time] 目标文件的最后修改时间
|
347
|
+
def copy_object(source, dest, opts = {})
|
348
|
+
@protocol.copy_object(name, source, dest, opts)
|
349
|
+
end
|
350
|
+
|
351
|
+
# 删除一个object
|
352
|
+
# @param key [String] Object的名字
|
353
|
+
def delete_object(key)
|
354
|
+
@protocol.delete_object(name, key)
|
355
|
+
end
|
356
|
+
|
357
|
+
# 批量删除object
|
358
|
+
# @param keys [Array<String>] Object的名字集合
|
359
|
+
# @param opts [Hash] 删除object的选项(可选)
|
360
|
+
# @option opts [Boolean] :quiet 指定是否允许Server返回成功删除的
|
361
|
+
# object
|
362
|
+
# @option opts [String] :encoding 指定Server返回的成功删除的
|
363
|
+
# object的名字的编码方式,目前只支持{OSS::KeyEncoding::URL}
|
364
|
+
# @return [Array<String>] 成功删除的object的名字,如果指定
|
365
|
+
# 了:quiet参数,则返回[]
|
366
|
+
def batch_delete_objects(keys, opts = {})
|
367
|
+
@protocol.batch_delete_objects(name, keys, opts)
|
368
|
+
end
|
369
|
+
|
370
|
+
# 设置object的ACL
|
371
|
+
# @param key [String] Object的名字
|
372
|
+
# @param acl [String] Object的{OSS::ACL ACL}
|
373
|
+
def set_object_acl(key, acl)
|
374
|
+
@protocol.put_object_acl(name, key, acl)
|
375
|
+
end
|
376
|
+
|
377
|
+
# 获取object的ACL
|
378
|
+
# @param key [String] Object的名字
|
379
|
+
# @return [String] object的{OSS::ACL ACL}
|
380
|
+
def get_object_acl(key)
|
381
|
+
@protocol.get_object_acl(name, key)
|
382
|
+
end
|
383
|
+
|
384
|
+
# 获取object的CORS规则
|
385
|
+
# @param key [String] Object的名字
|
386
|
+
# @return [OSS::CORSRule]
|
387
|
+
def get_object_cors(key)
|
388
|
+
@protocol.get_object_cors(name, key)
|
389
|
+
end
|
390
|
+
|
391
|
+
##
|
392
|
+
# 断点续传相关的API
|
393
|
+
#
|
394
|
+
|
395
|
+
# 上传一个本地文件到bucket中的一个object,支持断点续传。指定的文
|
396
|
+
# 件会被分成多个分片进行上传,只有所有分片都上传成功整个文件才
|
397
|
+
# 上传成功。
|
398
|
+
# @param key [String] Object的名字
|
399
|
+
# @param file [String] 本地文件的路径
|
400
|
+
# @param opts [Hash] 上传文件的可选项
|
401
|
+
# @option opts [String] :content_type 设置所上传的内容的
|
402
|
+
# Content-Type,默认是application/octet-stream
|
403
|
+
# @option opts [Hash] :metas 设置object的meta,这是一些用户自定
|
404
|
+
# 义的属性,它们会和object一起存储,在{#get_object_meta}的时候会
|
405
|
+
# 返回这些meta。属性的key不区分大小写。例如:{ 'year' => '2015' }
|
406
|
+
# @option opts [Integer] :part_size 设置分片上传时每个分片的大小,
|
407
|
+
# 默认为1 MB。断点上传最多允许10000个分片,如果文件大于10000个
|
408
|
+
# 分片的大小,则每个分片的大小会大于1MB。
|
409
|
+
# @option opts [String] :cpt_file 断点续传的checkpoint文件,如果
|
410
|
+
# 指定的cpt文件不存在,则会在file所在目录创建一个默认的cpt文件,
|
411
|
+
# 命名方式为:file.cpt,其中file是用户要上传的文件。在上传的过
|
412
|
+
# 程中会不断更新此文件,成功完成上传后会删除此文件;如果指定的
|
413
|
+
# cpt文件已存在,则从cpt文件中记录的点继续上传。
|
414
|
+
# @option opts [Boolean] :disable_cpt 是否禁用checkpoint功能,如
|
415
|
+
# 果设置为true,则在上传的过程中不会写checkpoint文件,这意味着
|
416
|
+
# 上传失败后不能断点续传,而只能重新上传整个文件。如果这个值为
|
417
|
+
# true,则:cpt_file会被忽略。
|
418
|
+
# @yield [Float] 如果调用的时候传递了block,则会将上传进度交由
|
419
|
+
# block处理,进度值是一个0-1之间的小数
|
420
|
+
# @raise [CheckpointBrokenError] 如果cpt文件被损坏,则抛出此错误
|
421
|
+
# @raise [FileInconsistentError] 如果指定的文件与cpt中记录的不一
|
422
|
+
# 致,则抛出此错误
|
423
|
+
# @example
|
424
|
+
# bucket.resumable_upload('my-object', '/tmp/x') do |p|
|
425
|
+
# puts "Progress: #{(p * 100).round(2)} %"
|
426
|
+
# end
|
427
|
+
def resumable_upload(key, file, opts = {}, &block)
|
428
|
+
unless cpt_file = opts[:cpt_file]
|
429
|
+
cpt_file = get_cpt_file(file)
|
430
|
+
end
|
431
|
+
|
432
|
+
Multipart::Upload.new(
|
433
|
+
@protocol, options: opts,
|
434
|
+
progress: block,
|
435
|
+
object: key, bucket: name, creation_time: Time.now,
|
436
|
+
file: File.expand_path(file), cpt_file: cpt_file
|
437
|
+
).run
|
438
|
+
end
|
439
|
+
|
440
|
+
# 下载bucket中的一个object到本地文件,支持断点续传。指定的object
|
441
|
+
# 会被分成多个分片进行下载,只有所有的分片都下载成功整个object才
|
442
|
+
# 下载成功。对于每个下载的分片,会在file所在目录建立一个临时文件
|
443
|
+
# file.part.N,下载成功后这些part文件会被合并成最后的file然后删
|
444
|
+
# 除。
|
445
|
+
# @param key [String] Object的名字
|
446
|
+
# @param file [String] 本地文件的路径
|
447
|
+
# @param opts [Hash] 下载文件的可选项
|
448
|
+
# @option opts [Integer] :part_size 设置分片上传时每个分片的大小,
|
449
|
+
# 默认为1 MB。断点下载最多允许100个分片,如果文件大于100个分片,
|
450
|
+
# 则每个分片的大小会大于1MB
|
451
|
+
# @option opts [String] :cpt_file 断点续传的checkpoint文件,如果
|
452
|
+
# 指定的cpt文件不存在,则会在file所在目录创建一个默认的cpt文件,
|
453
|
+
# 命名方式为:file.cpt,其中file是用户要下载的文件名。在下载的过
|
454
|
+
# 程中会不断更新此文件,成功完成下载后会删除此文件;如果指定的
|
455
|
+
# cpt文件已存在,则从cpt文件中记录的点继续下载。
|
456
|
+
# @option opts [Boolean] :disable_cpt 是否禁用checkpoint功能,如
|
457
|
+
# 果设置为true,则在下载的过程中不会写checkpoint文件,这意味着
|
458
|
+
# 下载失败后不能断点续传,而只能重新下载整个文件。如果这个值为true,
|
459
|
+
# 则:cpt_file会被忽略。
|
460
|
+
# @option opts [Hash] :condition 指定下载object需要满足的条件,
|
461
|
+
# 同 {#get_object}
|
462
|
+
# @option opts [Hash] :rewrite 指定下载object时Server端返回的响
|
463
|
+
# 应头部字段的值,同 {#get_object}
|
464
|
+
# @yield [Float] 如果调用的时候传递了block,则会将下载进度交由
|
465
|
+
# block处理,进度值是一个0-1之间的小数
|
466
|
+
# @raise [CheckpointBrokenError] 如果cpt文件被损坏,则抛出此错误
|
467
|
+
# @raise [ObjectInconsistentError] 如果指定的object的etag与cpt文
|
468
|
+
# 件中记录的不一致,则抛出错误
|
469
|
+
# @raise [PartMissingError] 如果已下载的部分(.part文件)找不到,
|
470
|
+
# 则抛出此错误
|
471
|
+
# @raise [PartInconsistentError] 如果已下载的部分(.part文件)的
|
472
|
+
# MD5值与cpt文件记录的不一致,则抛出此错误
|
473
|
+
# @note 已经下载的部分会在file所在的目录创建.part文件,命名方式
|
474
|
+
# 为file.part.N
|
475
|
+
# @example
|
476
|
+
# bucket.resumable_download('my-object', '/tmp/x') do |p|
|
477
|
+
# puts "Progress: #{(p * 100).round(2)} %"
|
478
|
+
# end
|
479
|
+
def resumable_download(key, file, opts = {}, &block)
|
480
|
+
unless cpt_file = opts[:cpt_file]
|
481
|
+
cpt_file = get_cpt_file(file)
|
482
|
+
end
|
483
|
+
|
484
|
+
Multipart::Download.new(
|
485
|
+
@protocol, options: opts,
|
486
|
+
progress: block,
|
487
|
+
object: key, bucket: name, creation_time: Time.now,
|
488
|
+
file: File.expand_path(file), cpt_file: cpt_file
|
489
|
+
).run
|
490
|
+
end
|
491
|
+
|
492
|
+
# 获取Bucket的URL
|
493
|
+
# @return [String] Bucket的URL
|
494
|
+
def bucket_url
|
495
|
+
@protocol.get_request_url(name)
|
496
|
+
end
|
497
|
+
|
498
|
+
# 获取Object的URL
|
499
|
+
# @param [String] key Object的key
|
500
|
+
# @param [Boolean] sign 是否对URL进行签名,默认为是
|
501
|
+
# @param [Fixnum] expiry URL的有效时间,单位为秒,默认为60s
|
502
|
+
# @return [String] 用于直接访问Object的URL
|
503
|
+
def object_url(key, sign = true, expiry = 60)
|
504
|
+
url = @protocol.get_request_url(name, key)
|
505
|
+
return url unless sign
|
506
|
+
|
507
|
+
expires = Time.now.to_i + expiry
|
508
|
+
string_to_sign = "GET\n" +
|
509
|
+
"\n\n" +
|
510
|
+
"#{expires}\n" +
|
511
|
+
"/#{name}/#{key}"
|
512
|
+
signature = sign(string_to_sign)
|
513
|
+
|
514
|
+
query_string = {
|
515
|
+
'Expires' => expires.to_s,
|
516
|
+
'OSSAccessKeyId' => CGI.escape(access_key_id),
|
517
|
+
'Signature' => CGI.escape(signature)
|
518
|
+
}.map { |k, v| "#{k}=#{v}" }.join('&')
|
519
|
+
|
520
|
+
[url, query_string].join('?')
|
521
|
+
end
|
522
|
+
|
523
|
+
# 获取用户所设置的ACCESS_KEY_ID
|
524
|
+
# @return [String] 用户的ACCESS_KEY_ID
|
525
|
+
def access_key_id
|
526
|
+
@protocol.get_access_key_id
|
527
|
+
end
|
528
|
+
|
529
|
+
# 用ACCESS_KEY_SECRET对内容进行签名
|
530
|
+
# @param [String] string_to_sign 要进行签名的内容
|
531
|
+
# @return [String] 生成的签名
|
532
|
+
def sign(string_to_sign)
|
533
|
+
@protocol.sign(string_to_sign)
|
534
|
+
end
|
535
|
+
|
536
|
+
private
|
537
|
+
# Infer the file's content type using MIME::Types
|
538
|
+
# @param file [String] the file path
|
539
|
+
# @return [String] the infered content type or nil if it fails
|
540
|
+
# to infer the content type
|
541
|
+
def get_content_type(file)
|
542
|
+
t = MIME::Types.of(file)
|
543
|
+
t.first.content_type unless t.empty?
|
544
|
+
end
|
545
|
+
|
546
|
+
# Get the checkpoint file path for file
|
547
|
+
# @param file [String] the file path
|
548
|
+
# @return [String] the checkpoint file path
|
549
|
+
def get_cpt_file(file)
|
550
|
+
"#{File.expand_path(file)}.cpt"
|
551
|
+
end
|
552
|
+
|
553
|
+
end # Bucket
|
554
|
+
end # OSS
|
555
|
+
end # Aliyun
|