aliyun-sdk 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+

|
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
|