cos 0.1.0 → 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 +4 -4
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.travis.yml +13 -2
- data/Gemfile +4 -1
- data/LICENSE +191 -0
- data/README.md +2014 -17
- data/Rakefile +23 -6
- data/bin/cos +325 -0
- data/bin/setup +1 -3
- data/cos.gemspec +24 -13
- data/lib/cos.rb +41 -4
- data/lib/cos/api.rb +289 -0
- data/lib/cos/bucket.rb +731 -0
- data/lib/cos/checkpoint.rb +62 -0
- data/lib/cos/client.rb +58 -0
- data/lib/cos/config.rb +102 -0
- data/lib/cos/dir.rb +301 -0
- data/lib/cos/download.rb +252 -0
- data/lib/cos/exception.rb +62 -0
- data/lib/cos/file.rb +152 -0
- data/lib/cos/http.rb +95 -0
- data/lib/cos/logging.rb +47 -0
- data/lib/cos/resource.rb +201 -0
- data/lib/cos/signature.rb +119 -0
- data/lib/cos/slice.rb +292 -0
- data/lib/cos/struct.rb +49 -0
- data/lib/cos/tree.rb +165 -0
- data/lib/cos/util.rb +82 -0
- data/lib/cos/version.rb +2 -2
- data/spec/cos/bucket_spec.rb +562 -0
- data/spec/cos/client_spec.rb +77 -0
- data/spec/cos/dir_spec.rb +195 -0
- data/spec/cos/download_spec.rb +105 -0
- data/spec/cos/http_spec.rb +70 -0
- data/spec/cos/signature_spec.rb +83 -0
- data/spec/cos/slice_spec.rb +302 -0
- data/spec/cos/struct_spec.rb +38 -0
- data/spec/cos/tree_spec.rb +322 -0
- data/spec/cos/util_spec.rb +106 -0
- data/test/download_test.rb +44 -0
- data/test/list_test.rb +43 -0
- data/test/upload_test.rb +48 -0
- metadata +132 -21
- data/.idea/.name +0 -1
- data/.idea/cos.iml +0 -49
- data/.idea/encodings.xml +0 -6
- data/.idea/misc.xml +0 -14
- data/.idea/modules.xml +0 -8
- data/.idea/workspace.xml +0 -465
- data/bin/console +0 -14
data/lib/cos/api.rb
ADDED
@@ -0,0 +1,289 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'rest-client'
|
5
|
+
|
6
|
+
module COS
|
7
|
+
|
8
|
+
# 腾讯云对象存储服务RestfulAPI
|
9
|
+
# @see {http://www.qcloud.com/wiki/RESTful_API%E6%96%87%E6%A1%A3}
|
10
|
+
class API
|
11
|
+
|
12
|
+
attr_reader :config, :http
|
13
|
+
|
14
|
+
# 初始化
|
15
|
+
#
|
16
|
+
# @example
|
17
|
+
# COS::API.new(config)
|
18
|
+
#
|
19
|
+
# @param config [COS::Config] 客户端设置
|
20
|
+
#
|
21
|
+
# @see COS::Config
|
22
|
+
def initialize(config)
|
23
|
+
@config = config
|
24
|
+
@http = COS::HTTP.new(config)
|
25
|
+
end
|
26
|
+
|
27
|
+
# 创建目录
|
28
|
+
#
|
29
|
+
# @see {http://www.qcloud.com/wiki/%E5%88%9B%E5%BB%BA%E7%9B%AE%E5%BD%95:}
|
30
|
+
#
|
31
|
+
# @param path [String] 目录路径, 如: 'path1', 'path1/path2', sdk会补齐末尾的 '/'
|
32
|
+
# @param options [Hash] 高级参数
|
33
|
+
# @option options [String] :biz_attr 目录属性, 业务端维护
|
34
|
+
# @option options [String] :bucket bucket名称
|
35
|
+
#
|
36
|
+
# @return Hash
|
37
|
+
# * :ctime [String] 创建时间Unix时间戳
|
38
|
+
# * :resource_path [String] 创建的资源路径
|
39
|
+
#
|
40
|
+
# @raise [ServerError] 服务端异常返回
|
41
|
+
def create_folder(path, options = {})
|
42
|
+
bucket = config.get_bucket(options[:bucket])
|
43
|
+
sign = http.signature.multiple(bucket)
|
44
|
+
payload = {op: 'create', biz_attr: options[:biz_attr]}
|
45
|
+
resource_path = Util.get_resource_path(config.app_id, bucket, path)
|
46
|
+
|
47
|
+
http.post(resource_path, {}, sign, payload.to_json)
|
48
|
+
.merge({name: resource_path.split('/').at(-1)})
|
49
|
+
end
|
50
|
+
|
51
|
+
# 上传文件(完整上传)
|
52
|
+
#
|
53
|
+
# @see {http://www.qcloud.com/wiki/%E5%88%9B%E5%BB%BA%E6%96%87%E4%BB%B6:_(%E5%AE%8C%E6%95%B4%E4%B8%8A%E4%BC%A0)}
|
54
|
+
#
|
55
|
+
# @param path [String] 目录路径, 如: '/', 'path1', 'path1/path2', sdk会补齐末尾的 '/'
|
56
|
+
# @param file_name [String] 文件名
|
57
|
+
# @param file_src [String] 本地文件路径
|
58
|
+
# @param options [Hash] 高级参数
|
59
|
+
# @option options [String] :biz_attr 目录属性, 业务端维护
|
60
|
+
# @option options [String] :bucket bucket名称
|
61
|
+
#
|
62
|
+
# @return Hash
|
63
|
+
# * :access_url [String] 生成的文件下载url
|
64
|
+
# * :url [String] 操作文件的url
|
65
|
+
# * :resource_path [String] 资源路径
|
66
|
+
#
|
67
|
+
# @raise [ServerError] 服务端异常返回
|
68
|
+
def upload(path, file_name, file_src, options = {})
|
69
|
+
bucket = config.get_bucket(options[:bucket])
|
70
|
+
sign = http.signature.multiple(bucket)
|
71
|
+
resource_path = Util.get_resource_path(config.app_id, bucket, path, file_name)
|
72
|
+
|
73
|
+
payload = {
|
74
|
+
op: 'upload',
|
75
|
+
sha: Util.file_sha1(file_src),
|
76
|
+
filecontent: File.new(file_src, 'rb'),
|
77
|
+
biz_attr: options[:biz_attr]
|
78
|
+
}
|
79
|
+
|
80
|
+
http.post(resource_path, {}, sign, payload)
|
81
|
+
end
|
82
|
+
|
83
|
+
# 上传文件(分片上传)
|
84
|
+
#
|
85
|
+
# @see {http://www.qcloud.com/wiki/%E5%88%9B%E5%BB%BA%E6%96%87%E4%BB%B6(%E5%88%86%E7%89%87%E4%B8%8A%E4%BC%A0,_%E7%AC%AC%E4%B8%80%E7%89%87):}
|
86
|
+
# @see {http://www.qcloud.com/wiki/%E5%88%9B%E5%BB%BA%E6%96%87%E4%BB%B6(%E5%88%86%E7%89%87%E4%B8%8A%E4%BC%A0,_%E5%90%8E%E7%BB%AD%E5%88%86%E7%89%87)}
|
87
|
+
#
|
88
|
+
# @param path [String] 目录路径, 如: '/', 'path1', 'path1/path2', sdk会补齐末尾的 '/'
|
89
|
+
# @param file_name [String] 文件名
|
90
|
+
# @param file_src [String] 本地文件路径
|
91
|
+
# @param options [Hash] 高级参数
|
92
|
+
# @option options [String] :biz_attr 目录属性, 业务端维护
|
93
|
+
# @option options [String] :bucket bucket名称
|
94
|
+
# @option options [Boolean] :disable_cpt 是否禁用checkpoint功能,如
|
95
|
+
# 果设置为true,则在上传的过程中不会写checkpoint文件,这意味着
|
96
|
+
# 上传失败后不能断点续传,而只能重新上传整个文件。如果这个值为
|
97
|
+
# true,则:cpt_file会被忽略。
|
98
|
+
# @option options [Integer] :threads 多线程上传线程数, 默认为10
|
99
|
+
# @option options [Integer] :slice_size 设置分片上传时每个分片的大小
|
100
|
+
# 默认为3 MB, 目前服务端最大限制也为3MB。
|
101
|
+
# @option options [String] :cpt_file 断点续传的checkpoint文件,如果
|
102
|
+
# 指定的cpt文件不存在,则会在file所在目录创建一个默认的cpt文件,
|
103
|
+
# 命名方式为:file.cpt,其中file是用户要下载的文件名。在下载的过
|
104
|
+
# 程中会不断更新此文件,成功完成下载后会删除此文件;如果指定的
|
105
|
+
# cpt文件已存在,则从cpt文件中记录的点继续下载。
|
106
|
+
#
|
107
|
+
# @yield [Float] 上传进度百分比回调, 进度值是一个0-1之间的小数
|
108
|
+
#
|
109
|
+
# @return Hash
|
110
|
+
# * :access_url [String] 生成的文件下载url
|
111
|
+
# * :url [String] 操作文件的url
|
112
|
+
# * :resource_path [String] 资源路径
|
113
|
+
#
|
114
|
+
# @raise [ServerError] 服务端异常返回
|
115
|
+
def upload_slice(path, file_name, file_src, options = {}, &block)
|
116
|
+
slice = Slice.new(
|
117
|
+
config: config,
|
118
|
+
http: http,
|
119
|
+
path: path,
|
120
|
+
file_name: file_name,
|
121
|
+
file_src: file_src,
|
122
|
+
options: options,
|
123
|
+
progress: block
|
124
|
+
).upload
|
125
|
+
|
126
|
+
{
|
127
|
+
access_url: slice[:access_url],
|
128
|
+
url: slice[:url],
|
129
|
+
resource_path: slice[:resource_path]
|
130
|
+
}
|
131
|
+
end
|
132
|
+
|
133
|
+
# 目录列表/前缀搜索
|
134
|
+
#
|
135
|
+
# @see {http://www.qcloud.com/wiki/%E7%9B%AE%E5%BD%95%E5%88%97%E8%A1%A8,%E5%89%8D%E7%BC%80%E6%90%9C%E7%B4%A2:}
|
136
|
+
#
|
137
|
+
# @param path [String] 目录路径, 如: '/', 'path1', 'path1/path2', sdk会补齐末尾的 '/'
|
138
|
+
# @param options [Hash]
|
139
|
+
# @option options [String] :bucket bucket名称
|
140
|
+
# @option options [String] :prefix 搜索前缀
|
141
|
+
# 如果填写prefix, 则列出含此前缀的所有文件及目录
|
142
|
+
# @option options [Integer] :num 每页拉取的数量, 默认20条
|
143
|
+
# @option options [Symbol] :pattern 获取方式
|
144
|
+
# :dir_only 只获取目录, :file_only 只获取文件, 默认为 :both 全部获取
|
145
|
+
# @option options [Symbol] :order 排序方式
|
146
|
+
# :asc 正序, :desc 倒序 默认为 :asc
|
147
|
+
# @option options [String] :context 页码
|
148
|
+
# 若需要翻页,需要将前一页返回值中的context透传到参数中
|
149
|
+
# 若order为:asc,则从当前页正序/往下翻页;若order为:desc,则从当前页倒序/往上翻
|
150
|
+
#
|
151
|
+
# @return Hash
|
152
|
+
# * :context [String] 透传字段,用于翻页,需要往前/往后翻页则透传回来
|
153
|
+
# * :has_more [Boolean] 是否有内容可以继续往前/往后翻页
|
154
|
+
# * :dircount [Integer] 子目录数量(总)
|
155
|
+
# * :filecount [Integer] 子文件数量(总)
|
156
|
+
# * :infos [Array<Hash>] 列表结果(可能为空)
|
157
|
+
# * * :name [String] 目录名/文件名
|
158
|
+
# * * :biz_attr [String] 目录/文件属性,业务端维护
|
159
|
+
# * * :filesize [Integer] 文件大小(当类型为文件时返回)
|
160
|
+
# * * :filelen [Integer] 文件已传输大小(通过与filesize对比可知文件传输进度,当类型为文件时返回)
|
161
|
+
# * * :sha [String] 文件sha1(当类型为文件时返回)
|
162
|
+
# * * :ctime [String] 创建时间(Unix时间戳)
|
163
|
+
# * * :mtime [String] 修改时间(Unix时间戳)
|
164
|
+
# * * :access_url [String] 生成的资源可访问的url(当类型为文件时返回)
|
165
|
+
#
|
166
|
+
# @raise [ServerError] 服务端异常返回
|
167
|
+
def list(path, options = {})
|
168
|
+
bucket = config.get_bucket(options[:bucket])
|
169
|
+
sign = http.signature.multiple(bucket)
|
170
|
+
resource_path = Util.get_resource_path(config.app_id, bucket, path, options[:prefix])
|
171
|
+
|
172
|
+
pattern = case options[:pattern].to_s.to_sym
|
173
|
+
when :dir_only
|
174
|
+
'eListDirOnly'
|
175
|
+
when :file_only
|
176
|
+
'eListFileOnly'
|
177
|
+
else
|
178
|
+
'eListBoth'
|
179
|
+
end
|
180
|
+
|
181
|
+
query = {
|
182
|
+
op: 'list',
|
183
|
+
num: options[:num] || 20,
|
184
|
+
pattern: pattern,
|
185
|
+
order: options[:order].to_s.to_sym == :desc ? 1 : 0,
|
186
|
+
context: options[:context]
|
187
|
+
}
|
188
|
+
|
189
|
+
http.get(resource_path, {params: query}, sign)
|
190
|
+
end
|
191
|
+
|
192
|
+
# 更新目录/文件信息(biz_attr)
|
193
|
+
#
|
194
|
+
# @see {http://www.qcloud.com/wiki/%E7%9B%AE%E5%BD%95/%E6%96%87%E4%BB%B6%E4%BF%A1%E6%81%AF_update}
|
195
|
+
#
|
196
|
+
# @param path [String] 资源路径,
|
197
|
+
# 如: 目录'path1/', 文件'path1/file'
|
198
|
+
# @param biz_attr [String] 目录/文件属性,业务端维护
|
199
|
+
# @param options [Hash]
|
200
|
+
# @option options [String] :bucket bucket名称
|
201
|
+
#
|
202
|
+
# @raise [ServerError] 服务端异常返回
|
203
|
+
def update(path, biz_attr, options = {})
|
204
|
+
bucket = config.get_bucket(options[:bucket])
|
205
|
+
resource_path = Util.get_resource_path_or_file(config.app_id, bucket, path)
|
206
|
+
sign = http.signature.once(bucket, path)
|
207
|
+
payload = {op: 'update', biz_attr: biz_attr}
|
208
|
+
|
209
|
+
http.post(resource_path, {}, sign, payload.to_json)
|
210
|
+
end
|
211
|
+
|
212
|
+
# 目录/文件信息查询
|
213
|
+
#
|
214
|
+
# @see {http://www.qcloud.com/wiki/%E7%9B%AE%E5%BD%95/%E6%96%87%E4%BB%B6%E4%BF%A1%E6%81%AF_%E6%9F%A5%E8%AF%A2}
|
215
|
+
#
|
216
|
+
# @param path [String] 资源路径, 如: 目录'path1/', 文件'path1/file'
|
217
|
+
# @param options [Hash]
|
218
|
+
# @option options [String] :bucket bucket名称
|
219
|
+
#
|
220
|
+
# @return Hash
|
221
|
+
# * :name [String] 目录名/文件名
|
222
|
+
# * :biz_attr [String] 目录/文件属性,业务端维护
|
223
|
+
# * :filesize [Integer] 文件大小(当类型为文件时返回)
|
224
|
+
# * :filelen [Integer] 文件已传输大小(通过与filesize对比可知文件传输进度,当类型为文件时返回)
|
225
|
+
# * :sha [String] 文件sha1(当类型为文件时返回)
|
226
|
+
# * :ctime [String] 创建时间(Unix时间戳)
|
227
|
+
# * :mtime [String] 修改时间(Unix时间戳)
|
228
|
+
# * :access_url [String] 生成的资源可访问的url(当类型为文件时返回)
|
229
|
+
#
|
230
|
+
# @raise [ServerError] 服务端异常返回
|
231
|
+
def stat(path, options = {})
|
232
|
+
bucket = config.get_bucket(options[:bucket])
|
233
|
+
sign = http.signature.multiple(bucket)
|
234
|
+
resource_path = Util.get_resource_path_or_file(config.app_id, bucket, path)
|
235
|
+
|
236
|
+
http.get(resource_path, {params: {op: 'stat'}}, sign)
|
237
|
+
end
|
238
|
+
|
239
|
+
# 删除文件及目录
|
240
|
+
#
|
241
|
+
# @see {http://www.qcloud.com/wiki/%E5%88%A0%E9%99%A4%E6%96%87%E4%BB%B6%E5%8F%8A%E7%9B%AE%E5%BD%95}
|
242
|
+
#
|
243
|
+
# @param path [String] 资源路径, 如: 目录'path1/', 文件'path1/file'
|
244
|
+
# @param options [Hash]
|
245
|
+
# @option options [String] :bucket bucket名称
|
246
|
+
#
|
247
|
+
# @raise [ServerError] 服务端异常返回
|
248
|
+
def delete(path, options = {})
|
249
|
+
bucket = config.get_bucket(options[:bucket])
|
250
|
+
resource_path = Util.get_resource_path_or_file(config.app_id, bucket, path)
|
251
|
+
sign = http.signature.once(bucket, path)
|
252
|
+
payload = {op: 'delete'}
|
253
|
+
|
254
|
+
http.post(resource_path, {}, sign, payload.to_json)
|
255
|
+
end
|
256
|
+
|
257
|
+
# 下载文件
|
258
|
+
#
|
259
|
+
# @note SDK会自动对私有读的Bucket进行签名
|
260
|
+
#
|
261
|
+
# @param access_url [String] 资源的下载URL地址可以从list,stat接口中获取
|
262
|
+
# @param file_store [String] 本地文件存储路径
|
263
|
+
# @param options [Hash]
|
264
|
+
# @option options [String] :bucket bucket名称
|
265
|
+
# @option options [Hash] :headers 设置下载请求头,如:range
|
266
|
+
#
|
267
|
+
# @raise [DownloadError] 下载失败,服务器返回状态异常
|
268
|
+
def download(access_url, file_store, options = {})
|
269
|
+
bucket = config.get_bucket(options[:bucket])
|
270
|
+
sign = http.signature.multiple(bucket)
|
271
|
+
|
272
|
+
response = RestClient::Request.execute(
|
273
|
+
:method => 'GET',
|
274
|
+
:url => "#{access_url}?sign=#{sign}",
|
275
|
+
:headers => options[:headers]
|
276
|
+
)
|
277
|
+
|
278
|
+
if response.code < 300
|
279
|
+
File.open(file_store, 'wb') do |w|
|
280
|
+
w.write(response.body)
|
281
|
+
end
|
282
|
+
else
|
283
|
+
raise DownloadError, "server response http code: #{response.code}"
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
end
|
288
|
+
|
289
|
+
end
|
data/lib/cos/bucket.rb
ADDED
@@ -0,0 +1,731 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
module COS
|
6
|
+
|
7
|
+
class Bucket
|
8
|
+
|
9
|
+
include Logging
|
10
|
+
|
11
|
+
attr_reader :client, :bucket_name, :authority, :bucket_type,
|
12
|
+
:migrate_source_domain, :need_preview, :refers,
|
13
|
+
:blackrefers, :brower_exec, :cnames, :nugc_flag
|
14
|
+
|
15
|
+
# 最小完整上传大小
|
16
|
+
MIN_UPLOAD_SLICE_SIZE = 10 * 1024 * 1024
|
17
|
+
|
18
|
+
# 最小下载分块大小
|
19
|
+
MIN_DOWNLOAD_SLICE_SIZE = 5 * 1024 * 1024
|
20
|
+
|
21
|
+
# 默认上传重试次数
|
22
|
+
DEFAULT_UPLOAD_RETRY = 10
|
23
|
+
|
24
|
+
# 默认下载重试次数
|
25
|
+
DEFAULT_DOWNLOAD_RETRY = 10
|
26
|
+
|
27
|
+
# 初始化
|
28
|
+
#
|
29
|
+
# @note SDK会自动获取bucket的信息,包括读取权限等并进行缓存
|
30
|
+
# 如需在后台修改了bucket信息请重新初始化Client
|
31
|
+
#
|
32
|
+
# @param client [COS::Client]
|
33
|
+
# @param bucket_name [String] bucket名称
|
34
|
+
# 如果在初始化时的配置中设置了default_bucket则该字段可以为空,会获取默认的bucket
|
35
|
+
#
|
36
|
+
# @return [COS::Bucket]DEFAULT_UPLOAD_RETRY
|
37
|
+
#
|
38
|
+
# @raise [ClientError] 未指定bucket
|
39
|
+
# @raise [ServerError] bucket不存在
|
40
|
+
def initialize(client, bucket_name = nil)
|
41
|
+
@client = client
|
42
|
+
@bucket_name = client.config.get_bucket(bucket_name)
|
43
|
+
|
44
|
+
# 使用stat API 获取根目录信息可获取到bucket信息
|
45
|
+
data = client.api.stat('/', bucket: bucket_name)
|
46
|
+
@authority = data[:authority]
|
47
|
+
@bucket_type = data[:bucket_type]
|
48
|
+
@need_preview = data[:need_preview]
|
49
|
+
@refers = data[:refers]
|
50
|
+
@migrate_source_domain = data[:migrate_source_domain]
|
51
|
+
@blackrefers = data[:blackrefers]
|
52
|
+
@brower_exec = data[:brower_exec]
|
53
|
+
@cnames = data[:cnames]
|
54
|
+
@nugc_flag = data[:nugc_flag]
|
55
|
+
end
|
56
|
+
|
57
|
+
# 创建目录
|
58
|
+
#
|
59
|
+
# @see API#create_folder
|
60
|
+
#
|
61
|
+
# @param path [String] 目录路径, 如: 'path1', 'path1/path2', sdk会补齐末尾的 '/'
|
62
|
+
# @param options [Hash] 高级参数
|
63
|
+
# @option options [String] :biz_attr 目录属性, 业务端维护
|
64
|
+
#
|
65
|
+
# @return [COS::COSDir]
|
66
|
+
#
|
67
|
+
# @raise [ServerError] 服务端异常返回
|
68
|
+
def create_folder(path, options = {})
|
69
|
+
data = client.api.create_folder(path, options.merge({bucket: bucket_name}))
|
70
|
+
dir = {
|
71
|
+
mtime: data[:mtime],
|
72
|
+
ctime: data[:ctime],
|
73
|
+
name: data[:name],
|
74
|
+
biz_attr: options[:biz_attr],
|
75
|
+
bucket: self,
|
76
|
+
path: path
|
77
|
+
}
|
78
|
+
|
79
|
+
COSDir.new(dir)
|
80
|
+
end
|
81
|
+
|
82
|
+
alias :mkdir :create_folder
|
83
|
+
|
84
|
+
# 获取list中的文件及目录个数
|
85
|
+
#
|
86
|
+
# @param path [String] 目录路径, 如: 'path1', 'path1/path2', sdk会补齐末尾的 '/'
|
87
|
+
#
|
88
|
+
# @param options [Hash]
|
89
|
+
# @option options [String] :prefix 搜索前缀
|
90
|
+
# 如果填写prefix, 则计算含此前缀的所有文件及目录个数
|
91
|
+
#
|
92
|
+
# @return [Hash]
|
93
|
+
# * :total [Integer] 文件和目录总数
|
94
|
+
# * :files [Integer] 文件数
|
95
|
+
# * :dirs [Integer] 目录数
|
96
|
+
#
|
97
|
+
# @raise [ServerError] 服务端异常返回
|
98
|
+
def list_count(path = '', options = {})
|
99
|
+
options = {}
|
100
|
+
result = client.api.list(path, options.merge({num: 1, bucket: bucket_name}))
|
101
|
+
total = result[:filecount] + result[:dircount]
|
102
|
+
|
103
|
+
{total: total, files: result[:filecount], dirs: result[:dircount]}
|
104
|
+
end
|
105
|
+
|
106
|
+
# 获取文件及目录总数
|
107
|
+
#
|
108
|
+
# @param path [String] 目录路径, 如: 'path1', 'path1/path2', sdk会补齐末尾的 '/'
|
109
|
+
#
|
110
|
+
# @return [Integer] 文件及目录总数
|
111
|
+
#
|
112
|
+
# @raise [ServerError] 服务端异常返回
|
113
|
+
def count(path = '')
|
114
|
+
lc = list_count(path)
|
115
|
+
lc[:total]
|
116
|
+
end
|
117
|
+
|
118
|
+
alias :size :count
|
119
|
+
|
120
|
+
# 获取文件数
|
121
|
+
#
|
122
|
+
# @param path [String] 目录路径, 如: 'path1', 'path1/path2', sdk会补齐末尾的 '/'
|
123
|
+
#
|
124
|
+
# @return [Integer] 文件数
|
125
|
+
#
|
126
|
+
# @raise [ServerError] 服务端异常返回
|
127
|
+
def count_files(path = '')
|
128
|
+
lc = list_count(path)
|
129
|
+
lc[:files]
|
130
|
+
end
|
131
|
+
|
132
|
+
# 获取目录数
|
133
|
+
#
|
134
|
+
# @param path [String] 目录路径, 如: 'path1', 'path1/path2', sdk会补齐末尾的 '/'
|
135
|
+
#
|
136
|
+
# @return [Integer] 目录数
|
137
|
+
#
|
138
|
+
# @raise [ServerError] 服务端异常返回
|
139
|
+
def count_dirs(path = '')
|
140
|
+
lc = list_count(path)
|
141
|
+
lc[:dirs]
|
142
|
+
end
|
143
|
+
|
144
|
+
# 列出目录
|
145
|
+
#
|
146
|
+
# @param path [String] 目录路径, 如: '/', 'path1', 'path1/path2', sdk会补齐末尾的 '/'
|
147
|
+
# @param options [Hash]
|
148
|
+
# @option options [String] :prefix 搜索前缀
|
149
|
+
# 如果填写prefix, 则列出含此前缀的所有文件及目录
|
150
|
+
# @option options [Integer] :num 每页拉取的数量, 默认20条
|
151
|
+
# @option options [Symbol] :pattern 获取方式
|
152
|
+
# :dir_only 只获取目录, :file_only 只获取文件, 默认为 :both 全部获取
|
153
|
+
# @option options [Symbol] :order 排序方式 :asc 正序, :desc 倒序 默认为 :asc
|
154
|
+
#
|
155
|
+
# @raise [ServerError] 服务端异常返回
|
156
|
+
#
|
157
|
+
# @return [Enumerator<Object>] 迭代器, 其中Object可能是COS::COSFile或COS::COSDir
|
158
|
+
#
|
159
|
+
# @example
|
160
|
+
# all = bucket.list
|
161
|
+
# all.each do |o|
|
162
|
+
# if o.is_a?(COS::COSFile)
|
163
|
+
# puts "File: #{o.name} #{o.format_size}"
|
164
|
+
# else
|
165
|
+
# puts "Dir: #{o.name} #{o.created_at}"
|
166
|
+
# end
|
167
|
+
# end
|
168
|
+
def list(path = '', options = {})
|
169
|
+
Resource.new(self, path, options).to_enum
|
170
|
+
end
|
171
|
+
|
172
|
+
alias :ls :list
|
173
|
+
|
174
|
+
# 上传文件, 大文件自动断点续传, 多线程上传
|
175
|
+
#
|
176
|
+
# @param path_or_dir [String|COS::COSDir] 目录路径或目录对象COSDir
|
177
|
+
# 目录路径如: '/', 'path1', 'path1/path2', sdk会补齐末尾的 '/'
|
178
|
+
# @param file_name [String] 文件名
|
179
|
+
# @param file_src [String] 本地文件路径
|
180
|
+
# @param options [Hash] 高级参数
|
181
|
+
# @option options [Boolean] :auto_create_folder 自动创建远端目录
|
182
|
+
# @option options [Integer] :min_slice_size 完整上传最小文件大小,
|
183
|
+
# 超过此大小将会使用分片多线程断点续传
|
184
|
+
# @option options [Integer] :upload_retry 上传重试次数, 默认10
|
185
|
+
# @option options [String] :biz_attr 目录属性, 业务端维护
|
186
|
+
# @option options [Boolean] :disable_cpt 是否禁用checkpoint功能,如
|
187
|
+
# 果设置为true,则在上传的过程中不会写checkpoint文件,这意味着
|
188
|
+
# 上传失败后不能断点续传,而只能重新上传整个文件。如果这个值为
|
189
|
+
# true,则:cpt_file会被忽略。
|
190
|
+
# @option options [Integer] :threads 多线程上传线程数, 默认为10
|
191
|
+
# @option options [Integer] :slice_size 设置分片上传时每个分片的大小
|
192
|
+
# 默认为3 MB, 目前服务端最大限制也为3MB。
|
193
|
+
# @option options [String] :cpt_file 断点续传的checkpoint文件,如果
|
194
|
+
# 指定的cpt文件不存在,则会在file所在目录创建一个默认的cpt文件,
|
195
|
+
# 命名方式为:file.cpt,其中file是用户要上传的文件名。在上传的过
|
196
|
+
# 程中会不断更新此文件,成功完成上传后会删除此文件;如果指定的
|
197
|
+
# cpt文件已存在,则从cpt文件中记录的点继续上传。
|
198
|
+
#
|
199
|
+
# @yield [Float] 上传进度百分比回调, 进度值是一个0-1之间的小数
|
200
|
+
#
|
201
|
+
# @raise [ServerError] 服务端异常返回
|
202
|
+
#
|
203
|
+
# @return [COS::COSFile]
|
204
|
+
#
|
205
|
+
# @example
|
206
|
+
# file = bucket.upload('path', 'file1', '~/test/file1') do |p|
|
207
|
+
# puts "上传进度: #{(p*100).round(2)}%")
|
208
|
+
# end
|
209
|
+
# puts file.url
|
210
|
+
def upload(path_or_dir, file_name, file_src, options = {}, &block)
|
211
|
+
dir = get_dir(path_or_dir, options[:auto_create_folder])
|
212
|
+
|
213
|
+
min_size = options[:min_slice_size] || MIN_UPLOAD_SLICE_SIZE
|
214
|
+
retry_times = options[:upload_retry] || DEFAULT_UPLOAD_RETRY
|
215
|
+
|
216
|
+
options.merge!({bucket: bucket_name})
|
217
|
+
file_src = File.expand_path(file_src)
|
218
|
+
file_size = File.size(file_src)
|
219
|
+
|
220
|
+
retry_loop(retry_times) do
|
221
|
+
if file_size > min_size
|
222
|
+
# 分块上传
|
223
|
+
client.api.upload_slice(dir.path, file_name, file_src, options, &block)
|
224
|
+
else
|
225
|
+
# 完整上传
|
226
|
+
client.api.upload(dir.path, file_name, file_src, options)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
# 获取上传完成文件的状态, 只会返回<COSFile>
|
231
|
+
stat(Util.get_list_path(dir.path, file_name, true))
|
232
|
+
end
|
233
|
+
|
234
|
+
# 批量上传目录下的全部文件(不包含子目录)
|
235
|
+
#
|
236
|
+
# @note 已存在的文件不会再次上传, 本地目录中的隐藏文件(已"."开头的)不会上传
|
237
|
+
# ".cpt"文件不会上传, 不会上传子目录
|
238
|
+
#
|
239
|
+
# @param path_or_dir [String|COS::COSDir] 目录路径或目录对象COSDir
|
240
|
+
# 目录路径如: '/', 'path1', 'path1/path2', sdk会补齐末尾的 '/'
|
241
|
+
# @param file_src_path [String] 本地文件夹路径
|
242
|
+
# @param options [Hash] 高级参数
|
243
|
+
# @option options [Boolean] :auto_create_folder 自动创建远端目录
|
244
|
+
# @option options [Integer] :min_slice_size 完整上传最小文件大小,
|
245
|
+
# 超过此大小将会使用分片多线程断点续传
|
246
|
+
# @option options [Integer] :upload_retry 上传重试次数, 默认10
|
247
|
+
# @option options [String] :biz_attr 目录属性, 业务端维护
|
248
|
+
# @option options [Boolean] :disable_cpt 是否禁用checkpoint功能,如
|
249
|
+
# 果设置为true,则在上传的过程中不会写checkpoint文件,这意味着
|
250
|
+
# 上传失败后不能断点续传,而只能重新上传整个文件。如果这个值为
|
251
|
+
# true,则:cpt_file会被忽略。
|
252
|
+
# @option options [Integer] :threads 多线程上传线程数, 默认为10
|
253
|
+
# @option options [Integer] :slice_size 设置分片上传时每个分片的大小
|
254
|
+
# 默认为3 MB, 目前服务端最大限制也为3MB。
|
255
|
+
# @option options [String] :cpt_file 断点续传的checkpoint文件,如果
|
256
|
+
# 指定的cpt文件不存在,则会在file所在目录创建一个默认的cpt文件,
|
257
|
+
# 命名方式为:file.cpt,其中file是用户要上传的文件名。在上传的过
|
258
|
+
# 程中会不断更新此文件,成功完成上传后会删除此文件;如果指定的
|
259
|
+
# cpt文件已存在,则从cpt文件中记录的点继续上传。
|
260
|
+
#
|
261
|
+
# @yield [Float] 上传进度百分比回调, 进度值是一个0-1之间的小数
|
262
|
+
#
|
263
|
+
# @raise [ServerError] 服务端异常返回
|
264
|
+
#
|
265
|
+
# @return [Array<COS::COSFile>]
|
266
|
+
#
|
267
|
+
# @example
|
268
|
+
# files = bucket.upload_all('path', '~/test') do |p|
|
269
|
+
# puts "上传进度: #{(p*100).round(2)}%")
|
270
|
+
# end
|
271
|
+
# files.each do |file|
|
272
|
+
# puts file.url
|
273
|
+
# end
|
274
|
+
def upload_all(path_or_dir, file_src_path, options = {}, &block)
|
275
|
+
local_path = Util.get_local_path(file_src_path, false)
|
276
|
+
uploaded = []
|
277
|
+
|
278
|
+
Dir.foreach(local_path) do |file|
|
279
|
+
|
280
|
+
if !file.start_with?('.') and !file.end_with?('.cpt') and !File.directory?(file)
|
281
|
+
logger.info("Begin to upload file >> #{file}")
|
282
|
+
|
283
|
+
begin
|
284
|
+
# 逐个上传
|
285
|
+
uploaded << upload(path_or_dir, file, "#{local_path}/#{file}", options, &block)
|
286
|
+
rescue => error
|
287
|
+
# 跳过错误
|
288
|
+
if options[:skip_error]
|
289
|
+
logger.info("#{file} error skipped")
|
290
|
+
next
|
291
|
+
else
|
292
|
+
# 终止上传抛出异常
|
293
|
+
raise error
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
logger.info("#{file} upload finished")
|
298
|
+
end
|
299
|
+
|
300
|
+
end
|
301
|
+
|
302
|
+
uploaded
|
303
|
+
end
|
304
|
+
|
305
|
+
# 获取文件或目录信息
|
306
|
+
#
|
307
|
+
# @note 如查询根目录('/', '')可以获取到bucket信息, 返回COSDir
|
308
|
+
#
|
309
|
+
# @param path [String] 资源路径, 如: 目录'path1/', 文件'path1/file'
|
310
|
+
#
|
311
|
+
# @raise [ServerError] 服务端异常返回
|
312
|
+
#
|
313
|
+
# @return [COSFile|COSDir] 如果是目录则返回COSDir资源对象,是文件则返回COSFile资源对象
|
314
|
+
#
|
315
|
+
# @example
|
316
|
+
# puts bucket.stat('path1/file').name
|
317
|
+
def stat(path = '')
|
318
|
+
data = client.api.stat(path, bucket: bucket_name)
|
319
|
+
|
320
|
+
# 查询'/'获取的是bucket信息, 无name参数, 需要补全
|
321
|
+
data[:name] = '' if data[:name].nil?
|
322
|
+
|
323
|
+
if data[:filesize].nil?
|
324
|
+
# 目录
|
325
|
+
COSDir.new(data.merge({bucket: self, path: path}))
|
326
|
+
else
|
327
|
+
# 文件
|
328
|
+
COSFile.new(data.merge({bucket: self, path: path}))
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
# 更新文件及目录业务属性
|
333
|
+
#
|
334
|
+
# @param path [String] 资源路径, 如: 目录'path1/', 文件'path1/file'
|
335
|
+
# @param biz_attr [String] 目录/文件属性,业务端维护
|
336
|
+
#
|
337
|
+
# @raise [ServerError] 服务端异常返回
|
338
|
+
#
|
339
|
+
# @example
|
340
|
+
# bucket.update('path1/file', 'i am the attr')
|
341
|
+
def update(path, biz_attr)
|
342
|
+
client.api.update(path, biz_attr, bucket: bucket_name)
|
343
|
+
end
|
344
|
+
|
345
|
+
# 删除文件或目录
|
346
|
+
#
|
347
|
+
# @note 非空目录及根目录不可删除,会抛出异常
|
348
|
+
#
|
349
|
+
# @param path [String] 资源路径, 如: 目录'path1/', 文件'path1/file'
|
350
|
+
#
|
351
|
+
# @raise [ServerError] 服务端异常返回
|
352
|
+
#
|
353
|
+
# @example
|
354
|
+
# bucket.delete('path1/file')
|
355
|
+
def delete(path)
|
356
|
+
client.api.delete(path, bucket: bucket_name)
|
357
|
+
end
|
358
|
+
|
359
|
+
# 删除文件或目录, 不会抛出异常而是返回布尔值
|
360
|
+
#
|
361
|
+
# @note 非空目录及根目录不可删除, 返回false
|
362
|
+
#
|
363
|
+
# @param path [String] 资源路径, 如: 目录'path1/', 文件'path1/file'
|
364
|
+
#
|
365
|
+
# @example
|
366
|
+
# puts bucket.delete!('path1/file')
|
367
|
+
def delete!(path)
|
368
|
+
delete(path)
|
369
|
+
true
|
370
|
+
rescue
|
371
|
+
false
|
372
|
+
end
|
373
|
+
|
374
|
+
# 目录是否是空的
|
375
|
+
#
|
376
|
+
# @param path [String] 资源路径, 如: 目录'path1/'
|
377
|
+
#
|
378
|
+
# @raise [ServerError] 服务端异常返回
|
379
|
+
#
|
380
|
+
# @return [Boolean] 是否为空
|
381
|
+
#
|
382
|
+
# @example
|
383
|
+
# puts bucket.empty?('path1/')
|
384
|
+
def empty?(path = '')
|
385
|
+
count(path) == 0
|
386
|
+
end
|
387
|
+
|
388
|
+
# 文件或目录是否存在
|
389
|
+
#
|
390
|
+
# @param path [String] 资源路径, 如: 目录'path1/', 文件'path1/file'
|
391
|
+
#
|
392
|
+
# @raise [ServerError] 服务端异常返回
|
393
|
+
#
|
394
|
+
# @return [Boolean] 是否存在
|
395
|
+
#
|
396
|
+
# @example
|
397
|
+
# puts bucket.exist?('path1/file1')
|
398
|
+
def exist?(path)
|
399
|
+
begin
|
400
|
+
stat(path)
|
401
|
+
rescue ServerError => e
|
402
|
+
return false if e.error_code == -166
|
403
|
+
raise e
|
404
|
+
end
|
405
|
+
|
406
|
+
true
|
407
|
+
end
|
408
|
+
|
409
|
+
alias :exists? :exist?
|
410
|
+
|
411
|
+
# 判断文件是否上传完成
|
412
|
+
#
|
413
|
+
# @param path [String] 文件资源路径, 如: 'path1/file'
|
414
|
+
#
|
415
|
+
# @raise [ServerError] 服务端异常返回
|
416
|
+
#
|
417
|
+
# @return [Boolean] 是否完成
|
418
|
+
#
|
419
|
+
# @example
|
420
|
+
# puts bucket.complete?('path1/file1')
|
421
|
+
def complete?(path)
|
422
|
+
get_file(path).complete?
|
423
|
+
end
|
424
|
+
|
425
|
+
# 获取文件可访问的URL
|
426
|
+
#
|
427
|
+
# @note 私有读取的bucket会自动生成带签名的URL
|
428
|
+
#
|
429
|
+
# @param path_or_file [String|COS::COSFile] 文件资源COSFile或路径, 如: 'path1/file'
|
430
|
+
# @param options [Hash] 高级参数
|
431
|
+
# @option options [String] :cname 在cos控制台设置的cname域名
|
432
|
+
# @option options [Boolean] :https 是否生成https的URL
|
433
|
+
# @option options [Integer] :expire_seconds 签名有效时间(秒,私有读取bucket时需要)
|
434
|
+
#
|
435
|
+
# @raise [ServerError] 服务端异常返回
|
436
|
+
#
|
437
|
+
# @return [String] 文件访问URL
|
438
|
+
#
|
439
|
+
# @example
|
440
|
+
# puts bucket.url('path1/file1', https: true, cname: 'static.domain.com')
|
441
|
+
def url(path_or_file, options = {})
|
442
|
+
|
443
|
+
file = get_file(path_or_file)
|
444
|
+
|
445
|
+
url = file.access_url
|
446
|
+
|
447
|
+
# 使用cname
|
448
|
+
if options[:cname]
|
449
|
+
host = URI.parse(url).host.downcase
|
450
|
+
url.gsub!(host, options[:cname])
|
451
|
+
end
|
452
|
+
|
453
|
+
# 使用https
|
454
|
+
if options[:https]
|
455
|
+
url.gsub!('http://', 'https://')
|
456
|
+
end
|
457
|
+
|
458
|
+
if authority == 'eWRPrivate'
|
459
|
+
# 私有读取的bucket自动生成带签名的URL
|
460
|
+
sign = client.signature.multiple(
|
461
|
+
bucket_name,
|
462
|
+
options[:expire_seconds] || client.config.multiple_sign_expire)
|
463
|
+
|
464
|
+
"#{url}?sign=#{sign}"
|
465
|
+
else
|
466
|
+
url
|
467
|
+
end
|
468
|
+
end
|
469
|
+
|
470
|
+
# 下载文件, 支持断点续传, 支持多线程
|
471
|
+
#
|
472
|
+
# @param path_or_file [String|COS::COSFile] 文件路径或文件对象COSFile
|
473
|
+
# @param file_store [String] 本地文件存储路径
|
474
|
+
# @param options [Hash] 高级参数
|
475
|
+
# @option options [Integer] :min_slice_size 完整下载最小文件大小,
|
476
|
+
# 超过此大小将会使用分片多线程断点续传
|
477
|
+
# @option options [Integer] :download_retry 下载重试次数, 默认10
|
478
|
+
# @option options [Boolean] :disable_cpt 是否禁用checkpoint功能,如
|
479
|
+
# 果设置为true,则在下载的过程中不会写checkpoint文件,这意味着
|
480
|
+
# 下载失败后不能断点续传,而只能重新下载整个文件。如果这个值为
|
481
|
+
# true,则:cpt_file会被忽略。
|
482
|
+
# @option options [Integer] :threads 多线程下载线程数, 默认为10
|
483
|
+
# @option options [Integer] :slice_size 设置分片下载时每个分片的大小
|
484
|
+
# 默认为5 MB。
|
485
|
+
# @option options [String] :cpt_file 断点续传的checkpoint文件,如果
|
486
|
+
# 指定的cpt文件不存在,则会在file所在目录创建一个默认的cpt文件,
|
487
|
+
# 命名方式为:file.cpt,其中file是用户要下载的文件名。在下载的过
|
488
|
+
# 程中会不断更新此文件,成功完成下载后会删除此文件;如果指定的
|
489
|
+
# cpt文件已存在,则从cpt文件中记录的点继续下载。
|
490
|
+
#
|
491
|
+
# @yield [Float] 下载进度百分比回调, 进度值是一个0-1之间的小数
|
492
|
+
#
|
493
|
+
# @raise [ServerError] 服务端异常返回
|
494
|
+
#
|
495
|
+
# @return [String]
|
496
|
+
#
|
497
|
+
# @example
|
498
|
+
# file = bucket.download('path/file1', '~/test/file1') do |p|
|
499
|
+
# puts "下载进度: #{(p*100).round(2)}%")
|
500
|
+
# end
|
501
|
+
# puts file
|
502
|
+
def download(path_or_file, file_store, options = {}, &block)
|
503
|
+
min_size = options[:min_slice_size] || MIN_DOWNLOAD_SLICE_SIZE
|
504
|
+
retry_times = options[:download_retry] || DEFAULT_DOWNLOAD_RETRY
|
505
|
+
|
506
|
+
# 如果传入的是一个路径需要先获取文件信息
|
507
|
+
file = get_file(path_or_file)
|
508
|
+
|
509
|
+
# 检查文件是否上传完整才能下载
|
510
|
+
unless file.complete?
|
511
|
+
raise FileUploadNotComplete, 'file upload not complete'
|
512
|
+
end
|
513
|
+
|
514
|
+
# 检查本地文件sha1是否一致, 如一致就已下载完成了
|
515
|
+
if file.sha1_match?(file_store)
|
516
|
+
logger.info("File #{file_store} exist and sha1 match, skip download.")
|
517
|
+
return file_store
|
518
|
+
end
|
519
|
+
|
520
|
+
retry_loop(retry_times) do
|
521
|
+
if file.filesize > min_size
|
522
|
+
# 分块下载
|
523
|
+
Download.new(
|
524
|
+
bucket: self,
|
525
|
+
cos_file: file,
|
526
|
+
file_store: file_store,
|
527
|
+
options: options,
|
528
|
+
progress: block
|
529
|
+
).download
|
530
|
+
|
531
|
+
else
|
532
|
+
# 直接下载
|
533
|
+
client.api.download(file.access_url, file_store, bucket: bucket_name)
|
534
|
+
|
535
|
+
end
|
536
|
+
end
|
537
|
+
|
538
|
+
# 返回本地文件路径
|
539
|
+
file_store
|
540
|
+
end
|
541
|
+
|
542
|
+
# 批量下载目录下的全部文件(不包含子目录)
|
543
|
+
#
|
544
|
+
# @note sdk会自动创建本地目录
|
545
|
+
#
|
546
|
+
# @param path_or_dir [String|COS::COSDir] 目录路径或目录对象COSDir
|
547
|
+
# 目录路径如: '/', 'path1', 'path1/path2', sdk会补齐末尾的 '/'
|
548
|
+
# @param file_store_path [String] 本地文件存储目录
|
549
|
+
# @param options [Hash] 高级参数
|
550
|
+
# @option options [Integer] :min_slice_size 完整下载最小文件大小,
|
551
|
+
# 超过此大小将会使用分片多线程断点续传
|
552
|
+
# @option options [Integer] :disable_mkdir 禁止自动创建本地文件夹, 默认会创建
|
553
|
+
# @option options [Integer] :download_retry 下载重试次数, 默认10
|
554
|
+
# @option options [Boolean] :disable_cpt 是否禁用checkpoint功能,如
|
555
|
+
# 果设置为true,则在下载的过程中不会写checkpoint文件,这意味着
|
556
|
+
# 下载失败后不能断点续传,而只能重新下载整个文件。如果这个值为
|
557
|
+
# true,则:cpt_file会被忽略。
|
558
|
+
# @option options [Integer] :threads 多线程下载线程数, 默认为10
|
559
|
+
# @option options [Integer] :slice_size 设置分片下载时每个分片的大小
|
560
|
+
# 默认为5 MB。
|
561
|
+
# @option options [String] :cpt_file 断点续传的checkpoint文件,如果
|
562
|
+
# 指定的cpt文件不存在,则会在file所在目录创建一个默认的cpt文件,
|
563
|
+
# 命名方式为:file.cpt,其中file是用户要下载的文件名。在下载的过
|
564
|
+
# 程中会不断更新此文件,成功完成下载后会删除此文件;如果指定的
|
565
|
+
# cpt文件已存在,则从cpt文件中记录的点继续下载。
|
566
|
+
#
|
567
|
+
# @yield [Float] 下载进度百分比回调, 进度值是一个0-1之间的小数
|
568
|
+
#
|
569
|
+
# @raise [ServerError] 服务端异常返回
|
570
|
+
#
|
571
|
+
# @return [Array<String>] 本地文件路径数组
|
572
|
+
#
|
573
|
+
# @example
|
574
|
+
# files = bucket.download_all('path/', '~/test/') do |p|
|
575
|
+
# puts "下载进度: #{(p*100).round(2)}%")
|
576
|
+
# end
|
577
|
+
# puts files
|
578
|
+
def download_all(path_or_dir, file_store_path, options = {}, &block)
|
579
|
+
local_path = Util.get_local_path(file_store_path, options[:disable_mkdir])
|
580
|
+
dir = get_dir(path_or_dir)
|
581
|
+
downloaded = []
|
582
|
+
|
583
|
+
# 遍历目录下的所有文件
|
584
|
+
dir.list(pattern: :file_only).each do |file|
|
585
|
+
logger.info("Begin to download file >> #{file.name}")
|
586
|
+
|
587
|
+
downloaded << file.download("#{local_path}/#{file.name}", options, &block)
|
588
|
+
|
589
|
+
logger.info("#{file.name} download finished")
|
590
|
+
end
|
591
|
+
|
592
|
+
downloaded
|
593
|
+
end
|
594
|
+
|
595
|
+
# 获取目录树形结构
|
596
|
+
#
|
597
|
+
# @param path_or_dir [String|COS::COSDir] 目录路径或目录对象COSDir
|
598
|
+
# 目录路径如: '/', 'path1', 'path1/path2', sdk会补齐末尾的 '/'
|
599
|
+
# @param options [Hash]
|
600
|
+
# @option options [Integer] :depth 子目录深度,默认为5
|
601
|
+
#
|
602
|
+
# @raise [ServerError] 服务端异常返回
|
603
|
+
#
|
604
|
+
# @return [Hash]
|
605
|
+
#
|
606
|
+
# @example
|
607
|
+
# tree = bucket.tree
|
608
|
+
# puts tree[:resource].name
|
609
|
+
# tree[:children].each do |r|
|
610
|
+
# puts r[:resource].name
|
611
|
+
# end
|
612
|
+
#
|
613
|
+
# {
|
614
|
+
# :resource => resource,
|
615
|
+
# :children => [
|
616
|
+
# {:resource => resource, :children => [...]},
|
617
|
+
# {:resource => resource, :children => [...]},
|
618
|
+
# ...
|
619
|
+
# ]
|
620
|
+
# }
|
621
|
+
def tree(path_or_dir = '', options = {})
|
622
|
+
dir = get_dir(path_or_dir)
|
623
|
+
Tree.new(options.merge({path: dir})).to_object
|
624
|
+
end
|
625
|
+
|
626
|
+
# 获取Hash格式的目录树形结构, 可用于直接to_json
|
627
|
+
#
|
628
|
+
# @param path_or_dir [String|COS::COSDir] 目录路径或目录对象COSDir
|
629
|
+
# 目录路径如: '/', 'path1', 'path1/path2', sdk会补齐末尾的 '/'
|
630
|
+
# @param options [Hash]
|
631
|
+
# @option options [Integer] :depth 子目录深度,默认为5
|
632
|
+
#
|
633
|
+
# @raise [ServerError] 服务端异常返回
|
634
|
+
#
|
635
|
+
# @return [Hash<Object>]
|
636
|
+
#
|
637
|
+
# @example
|
638
|
+
# puts bucket.hash_tree.to_json
|
639
|
+
#
|
640
|
+
# {
|
641
|
+
# :resource => {name: '', mtime: ''...},
|
642
|
+
# :children => [
|
643
|
+
# {:resource => resource, :children => [...]},
|
644
|
+
# {:resource => resource, :children => [...]},
|
645
|
+
# ...
|
646
|
+
# ]
|
647
|
+
# }
|
648
|
+
def hash_tree(path_or_dir = '', options = {})
|
649
|
+
dir = get_dir(path_or_dir)
|
650
|
+
Tree.new(options.merge({path: dir})).to_hash
|
651
|
+
end
|
652
|
+
|
653
|
+
private
|
654
|
+
|
655
|
+
# 重试循环
|
656
|
+
def retry_loop(retry_times, &block)
|
657
|
+
begin
|
658
|
+
block.call
|
659
|
+
rescue => error
|
660
|
+
|
661
|
+
if retry_times > 0
|
662
|
+
retry_times -= 1
|
663
|
+
logger.info('Retrying...')
|
664
|
+
retry
|
665
|
+
else
|
666
|
+
raise error
|
667
|
+
end
|
668
|
+
|
669
|
+
end
|
670
|
+
end
|
671
|
+
|
672
|
+
# 获取文件对象, 可接受path string或COSFile
|
673
|
+
def get_file(path_or_file)
|
674
|
+
if path_or_file.is_a?(COS::COSFile)
|
675
|
+
# 传入的是COSFile
|
676
|
+
path_or_file
|
677
|
+
|
678
|
+
elsif path_or_file.is_a?(String)
|
679
|
+
# 传入的是path string
|
680
|
+
file = stat(path_or_file)
|
681
|
+
get_file(file)
|
682
|
+
|
683
|
+
else
|
684
|
+
raise ClientError,
|
685
|
+
"can't get file from #{path_or_file.class}, " \
|
686
|
+
'must be a file path string or COS::COSFile'
|
687
|
+
|
688
|
+
end
|
689
|
+
end
|
690
|
+
|
691
|
+
# 获取目录对象, 可接受path string或COSDir
|
692
|
+
def get_dir(path_or_dir, auto_create_folder = false)
|
693
|
+
if path_or_dir.is_a?(COS::COSDir)
|
694
|
+
# 传入的是COSDir
|
695
|
+
path_or_dir
|
696
|
+
|
697
|
+
elsif path_or_dir.is_a?(String)
|
698
|
+
# 传入的是path string
|
699
|
+
path_or_dir = "#{path_or_dir}/" unless path_or_dir.end_with?('/')
|
700
|
+
|
701
|
+
dir = handle_folder(path_or_dir, auto_create_folder)
|
702
|
+
get_dir(dir)
|
703
|
+
|
704
|
+
else
|
705
|
+
raise ClientError,
|
706
|
+
"can't get dir from #{path_or_dir.class}, " \
|
707
|
+
'must be a file path string or COS::COSDir'
|
708
|
+
|
709
|
+
end
|
710
|
+
end
|
711
|
+
|
712
|
+
# 获取目录信息并可以自动创建目录
|
713
|
+
def handle_folder(path_or_dir, auto_create_folder = false)
|
714
|
+
return stat(path_or_dir)
|
715
|
+
rescue => error
|
716
|
+
unless auto_create_folder
|
717
|
+
raise error
|
718
|
+
end
|
719
|
+
|
720
|
+
# 自动创建目录
|
721
|
+
if error.is_a?(COS::ServerError) and error.error_code == -166
|
722
|
+
logger.info('path not exist, auto create folder...')
|
723
|
+
return create_folder(path_or_dir)
|
724
|
+
end
|
725
|
+
|
726
|
+
raise error
|
727
|
+
end
|
728
|
+
|
729
|
+
end
|
730
|
+
|
731
|
+
end
|