qcloud_cos 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 725d8ee2d23b4c1680e6af13c12936afedffd50a
4
- data.tar.gz: 77446b32de74008c5d1a31ab665e249df72fbc4d
3
+ metadata.gz: c46f40136350e35780ce86e5a216af26a75ec799
4
+ data.tar.gz: a90fe51412a6a35a83188e8509d7f51d48b469ec
5
5
  SHA512:
6
- metadata.gz: 6f48665e19689e1565beba79216581a69ff4e55bcd426c24a16fbce545c4c3c32c59653fa7ce8f6acdecc00a85e1a4343c3ba5f0b55b05cf80b1f01af3cc1851
7
- data.tar.gz: 08c879544700e8b0ac76d9b1d9aa7a8d9a9fe4058ded37fbe65f2cdc2f30c727dcb5348e8018484785589c2949131e3d94935c3f32179b5db06936a3babcc2cc
6
+ metadata.gz: 9d889e680582184954f7bb93260762f9406acf285b7c7bb70ff439992bda8157e8e8c19cecc7e16ee367f2786982861d4decc8fe110bf9e73dbf4d38c4af0f30
7
+ data.tar.gz: ffd68ec5201c615ba2d12598a4595148214260a0a90f30d03c837b318768e0a5651be14e97197bcc2154854c8b298f05d4fb278c1ad416e6fa4e3068f7a91595
@@ -0,0 +1,37 @@
1
+ # This is the configuration used to check the rubocop source code.
2
+
3
+ AllCops:
4
+ Exclude:
5
+ - 'demo/**/*'
6
+ - 'test/**/*'
7
+
8
+ Metrics/LineLength:
9
+ Max: 100
10
+ Enabled: false
11
+
12
+ Style/Documentation:
13
+ Enabled: false
14
+
15
+ Style/DoubleNegation:
16
+ Enabled: false
17
+
18
+ Metrics/ClassLength:
19
+ Enabled: false
20
+
21
+ Style/AccessorMethodName:
22
+ Enabled: false
23
+
24
+ Style/PredicateName:
25
+ Enabled: false
26
+
27
+ Style/Lambda:
28
+ Enabled: false
29
+
30
+ Lint/UnusedMethodArgument:
31
+ Enabled: false
32
+
33
+ Style/AsciiComments:
34
+ Enabled: false
35
+
36
+ Metrics/ModuleLength:
37
+ Enabled: false
@@ -1,4 +1,7 @@
1
1
  language: ruby
2
2
  rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.0
3
6
  - 2.2.0
4
- before_install: gem install bundler -v 1.10.6
7
+ before_install: gem install bundler
data/Gemfile CHANGED
@@ -2,3 +2,4 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in qcloud_cos.gemspec
4
4
  gemspec
5
+ gem 'simplecov'
data/README.md CHANGED
@@ -1,46 +1,79 @@
1
- # Qcloud Cos
2
-
3
- It's the full featured library for qcloud cos, Enjoy it!
4
-
5
- ## Installation
6
-
7
- Add this line to your application's Gemfile:
8
-
9
- ```ruby
10
- gem 'qcloud_cos'
11
- ```
12
-
13
- And then execute:
14
-
15
- $ bundle
16
-
17
- Or install it yourself as:
18
-
19
- $ gem install qcloud_cos
20
-
21
- ## Usage
22
-
23
- QCloud::Cos.configure do |config|
24
- config.app_id = ""
25
- config.access_key = ""
26
- config.secret_key = ""
27
- config.default_bucket = "bucket-name"
28
- end
29
-
30
- QCloud::Cos.list_buckets
31
-
32
- QCloud::Cos.buckets.list
33
-
34
- QCloud::Cos.bucket.files/folders
35
-
36
- QCloud::Cos.bucket.upload
37
-
38
- ## Development
39
-
40
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
41
-
42
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
43
-
44
- ## Contributing
45
-
46
- Bug reports and pull requests are welcome on GitHub at https://github.com/zlx/qcloud_cos.
1
+ # Qcloud COS
2
+
3
+ It's the full featured Ruby SDK for Qcloud COS(Cloud Object Service).
4
+
5
+ We keep API simple but powerful, to give you more freedom.
6
+
7
+ Enjoy it!
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+
14
+ gem 'qcloud_cos'
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install qcloud_cos
23
+
24
+ ## Usage
25
+
26
+ ```ruby
27
+ QcloudCos.configure do |config|
28
+ config.app_id = 'app-id'
29
+ config.secret_id = 'secret_id'
30
+ config.secret_key = 'secret_key'
31
+ config.endpoint = "http://web.file.myqcloud.com/files/v1/"
32
+ config.bucket = "default-bucket-name"
33
+ end
34
+
35
+ QcloudCos.list # 列出 / 目录下的文件和文件夹
36
+
37
+ QcloudCos.upload('/test.log', 'Hello World')
38
+ QcloudCos.upload('/test.log', File.new('path/to/log'))
39
+
40
+ QcloudCos.upload_slice('/video.mp4', 'path/to/video.mp4')
41
+
42
+ QcloudCos.create_folder('/test/') # 创建目录
43
+ ```
44
+
45
+ More Example and Scenario, visit our [Document](#document)
46
+
47
+
48
+ ## Document
49
+
50
+ Here is original Restful API, It has the most detailed and authoritative explanation for every API.
51
+
52
+ + [COS RESTful API文档](http://www.qcloud.com/wiki/RESTful_API%E6%96%87%E6%A1%A3)
53
+ + [COS 详细文档](http://www.qcloud.com/doc/product/227/%E4%BA%A7%E5%93%81%E4%BB%8B%E7%BB%8D)
54
+
55
+ Here is our RDoc Document, It's well format to help you find more detail about methods.
56
+
57
+ + [RDoc Document](http://www.rubydoc.info/gems/qcloud_cos/0.1.0)
58
+
59
+
60
+ Here are some more guides for help you. Welcome to advice.
61
+
62
+ + [Getting Started](http://git.oschina.net/newell_zlx/cos_ruby_sdk/blob/master/wiki/get_started.md)
63
+
64
+
65
+ ## Test
66
+
67
+ We use minitest for test and rubocop for Syntax checker, If you want to make contribute to this library. Confirm below Command is success:
68
+
69
+ bundle exec rake test
70
+
71
+
72
+ ## Authors && Contributors
73
+
74
+ - [Newell](https://github.com/zlx_star)
75
+
76
+
77
+ ## License
78
+
79
+ licensed under the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0.html)
data/Rakefile CHANGED
@@ -1,10 +1,20 @@
1
- require "bundler/gem_tasks"
2
- require "rake/testtask"
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+ require 'rubocop/rake_task'
3
4
 
4
5
  Rake::TestTask.new(:test) do |t|
5
- t.libs << "test"
6
- t.libs << "lib"
6
+ t.libs << 'test'
7
+ t.libs << 'lib'
7
8
  t.test_files = FileList['test/**/*_test.rb']
8
9
  end
9
10
 
10
- task :default => :test
11
+ task default: :test
12
+
13
+ task :test do
14
+ Rake::Task['test'].invoke
15
+ # Rake::Task['rubocop'].invoke
16
+ end
17
+
18
+ RuboCop::RakeTask.new do |task|
19
+ task.fail_on_error = false
20
+ end
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require "bundler/setup"
4
- require "qcloud_cos"
3
+ require 'bundler/setup'
4
+ require 'qcloud_cos'
5
5
 
6
6
  # You can add fixtures and/or initialization code here to make experimenting
7
7
  # with your gem easier. You can also use a different console, if you like.
@@ -10,5 +10,14 @@ require "qcloud_cos"
10
10
  # require "pry"
11
11
  # Pry.start
12
12
 
13
- require "irb"
13
+ QcloudCos.configure do |config|
14
+ config.app_id = ENV['QCLOUD_APP_ID'] || 'app-id'
15
+ config.secret_id = ENV['QCLOUD_SECRET_ID'] || 'secret_id'
16
+ config.secret_key = ENV['QCLOUD_SECRET_KEY'] || 'secret_key'
17
+ config.endpoint = ENV['QCLOUD_ENDPOINT'] || 'https://web.file.myqcloud.com/files/v1/'
18
+ config.bucket = ENV['QCLOUD_BUCKET'] || 'privatesdkdemo'
19
+ config.ssl_ca_file = ENV['SSL_CA_FILE'] || 'path/to/ssl/ca/file'
20
+ end
21
+
22
+ require 'irb'
14
23
  IRB.start
@@ -1,5 +1,66 @@
1
- require "qcloud_cos/version"
1
+ require 'qcloud_cos/version'
2
+ require 'qcloud_cos/configuration'
3
+ require 'qcloud_cos/authorization'
4
+ require 'qcloud_cos/http'
5
+ require 'qcloud_cos/api'
6
+ require 'qcloud_cos/convenient_api'
2
7
 
3
8
  module QcloudCos
4
- # Your code goes here...
9
+ EXPIRED_SECONDS = 60 # 60 seconds
10
+ PUBLIC_EXPIRED_SECONDS = 600 # 10 minutes
11
+ DEFAULT_SLICE_SIZE = 3_145_728 # 3M
12
+ MIN_SLICE_FILE_SIZE = 10 # 10M
13
+ MAX_RETRY_TIMES = 3
14
+
15
+ class << self
16
+ include Api
17
+ include ConvenientApi
18
+
19
+ def configure
20
+ @configuration ||= Configuration.new
21
+ yield @configuration
22
+ @configuration
23
+ end
24
+
25
+ def config
26
+ @configuration
27
+ end
28
+
29
+ private
30
+
31
+ def http
32
+ @http ||= Http.new(config)
33
+ end
34
+
35
+ def authorization
36
+ @authorization ||= Authorization.new(QcloudCos.config)
37
+ end
38
+
39
+ def validates(path, options, path_validate = :file_only)
40
+ Utils.stringify_keys!(options)
41
+ file_validates(path, path_validate)
42
+
43
+ bucket = options['bucket'] || config.bucket
44
+ fail MissingBucketError unless bucket
45
+
46
+ bucket
47
+ end
48
+
49
+ def fixed_path(path)
50
+ path.start_with?('/') ? path : "/#{path}"
51
+ end
52
+
53
+ def generate_rest_url(bucket, path)
54
+ "#{config.endpoint}#{config.app_id}/#{bucket}#{path}"
55
+ end
56
+
57
+ def file_validates(path, path_validate)
58
+ case path_validate.to_s
59
+ when 'file_only'
60
+ fail InvalidFilePathError if path.end_with?('/')
61
+ when 'folder_only'
62
+ FolderObject.validate(path)
63
+ end
64
+ end
65
+ end
5
66
  end
@@ -0,0 +1,365 @@
1
+ # encoding: utf-8
2
+ require 'qcloud_cos/utils'
3
+ require 'qcloud_cos/model/list'
4
+
5
+ module QcloudCos
6
+ module Api
7
+ # 列出所有文件或者目录
8
+ #
9
+ # @param path [String] 指定目标路径, 以 / 结尾, 则列出该目录下文件或者文件夹,不以 / 结尾,就搜索该前缀的文件或者文件夹
10
+ # @param options [Hash] 额外参数
11
+ # @option options [String] :bucket (config.bucket_name) 指定当前 bucket, 默认是配置里面的 bucket
12
+ # @option options [Integer] :num (100) 指定需要拉取的条目
13
+ # @option options [String] :pattern (eListBoth) 指定拉取的内容,可选值: eListBoth, eListDirOnly, eListFileOnly
14
+ # @option options [Integer] :order (0) 指定拉取文件的顺序, 默认为正序(=0), 可选值: 0, 1
15
+ # @option options [String] :context ("") 透传字段,查看第一页,则传空字符串。若需要翻页,需要将前一页返回值中的context透传到参数中。order用于指定翻页顺序。若order填0,则从当前页正序/往下翻页;若order填1,则从当前页倒序/往上翻页。
16
+ #
17
+ # @return [Hash]
18
+ def list(path = '/', options = {})
19
+ path = fixed_path(path)
20
+ bucket = validates(path, options, 'both')
21
+
22
+ query = {
23
+ 'op' => 'list',
24
+ 'num' => 100
25
+ }.merge(Utils.hash_slice(options, 'num', 'pattern', 'order', 'context'))
26
+
27
+ url = generate_rest_url(bucket, path)
28
+ sign = authorization.sign(bucket)
29
+
30
+ result = http.get(url, query: query, headers: { 'Authorization' => sign }).parsed_response
31
+ QcloudCos::List.new(result['data'])
32
+ end
33
+
34
+ # 列出所有文件
35
+ #
36
+ # @param path [String] 指定目标路径, 以 / 结尾, 则列出该目录下文件,不以 / 结尾,就搜索该前缀的文件
37
+ # @param options [Hash] 额外参数
38
+ # @option options [String] :bucket (config.bucket_name) 指定当前 bucket, 默认是配置里面的 bucket
39
+ # @option options [Integer] :num (100) 指定需要拉取的条目
40
+ # @option options [Integer] :order (0) 指定拉取文件的顺序, 默认为正序(=0), 可选值: 0, 1
41
+ # @option options [String] :context ("") 透传字段,查看第一页,则传空字符串。若需要翻页,需要将前一页返回值中的context透传到参数中。order用于指定翻页顺序。若order填0,则从当前页正序/往下翻页;若order填1,则从当前页倒序/往上翻页。
42
+ #
43
+ # @return [Hash]
44
+ def list_files(path = '/', options = {})
45
+ Utils.stringify_keys!(options)
46
+ list(path, options.merge('pattern' => 'eListFileOnly'))
47
+ end
48
+
49
+ # 列出所有目录
50
+ #
51
+ # @param path [String] 指定目标路径, 以 / 结尾, 则列出该目录下文件夹,不以 / 结尾,就搜索该前缀的文件夹
52
+ # @param options [Hash] 额外参数
53
+ # @option options [String] :bucket (config.bucket_name) 指定当前 bucket, 默认是配置里面的 bucket
54
+ # @option options [Integer] :num (100) 指定需要拉取的条目
55
+ # @option options [Integer] :order (0) 指定拉取文件的顺序, 默认为正序(=0), 可选值: 0, 1
56
+ # @option options [String] :context ("") 透传字段,查看第一页,则传空字符串。若需要翻页,需要将前一页返回值中的context透传到参数中。order用于指定翻页顺序。若order填0,则从当前页正序/往下翻页;若order填1,则从当前页倒序/往上翻页。
57
+ #
58
+ # @return [Hash]
59
+ def list_folders(path = '/', options = {})
60
+ Utils.stringify_keys!(options)
61
+ list(path, options.merge('pattern' => 'eListDirOnly'))
62
+ end
63
+
64
+ # 创建目录
65
+ #
66
+ # @param path [String] 指定要创建的文件夹名字,支持级联创建
67
+ # @param options [Hash] options
68
+ # @option options [String] :bucket (config.bucket_name) 指定当前 bucket, 默认是配置里面的 bucket
69
+ # @option options [Integer] :biz_attr 指定目录的 biz_attr 由业务端维护, 会在文件信息中返回
70
+ #
71
+ # @return [Hash]
72
+ def create_folder(path, options = {})
73
+ path = fixed_path(path)
74
+ bucket = validates(path, options, :folder_only)
75
+
76
+ url = generate_rest_url(bucket, path)
77
+
78
+ query = { 'op' => 'create' }.merge(Utils.hash_slice(options, 'biz_attr'))
79
+
80
+ headers = {
81
+ 'Authorization' => authorization.sign(bucket),
82
+ 'Content-Type' => 'application/json'
83
+ }
84
+
85
+ http.post(url, body: query.to_json, headers: headers).parsed_response
86
+ end
87
+
88
+ # 上传文件
89
+ #
90
+ # @param path [String] 指定上传文件的路径
91
+ # @param file_or_bin [File||String] 指定文件或者文件内容
92
+ # @param options [Hash] options
93
+ # @option options [String] :bucket (config.bucket_name) 指定当前 bucket, 默认是配置里面的 bucket
94
+ # @option options [Integer] :biz_attr 指定文件的 biz_attr 由业务端维护, 会在文件信息中返回
95
+ #
96
+ # @return [Hash]
97
+ def upload(path, file_or_bin, options = {})
98
+ path = fixed_path(path)
99
+ bucket = validates(path, options)
100
+
101
+ url = generate_rest_url(bucket, path)
102
+
103
+ query = {
104
+ 'op' => 'upload'
105
+ }.merge(Utils.hash_slice(options, 'biz_attr')).merge(generate_file_query(file_or_bin))
106
+
107
+ http.post(url, query: query, headers: { 'Authorization' => authorization.sign(bucket) }).parsed_response
108
+ end
109
+ alias_method :create, :upload
110
+
111
+ # 分片上传
112
+ #
113
+ # @param dst_path [String] 指定文件的目标路径
114
+ # @param src_path [String] 指定文件的本地路径
115
+ # @param options [Hash] options
116
+ # @option options [String] :bucket (config.bucket_name) 指定当前 bucket, 默认是配置里面的 bucket
117
+ # @option options [Integer] :biz_attr 指定文件的 biz_attr 由业务端维护, 会在文件信息中返回
118
+ # @option options [Integer] :session 指定本次分片上传的 session
119
+ # @option options [Integer] :slice_size 指定分片大小
120
+ #
121
+ # @raise [MissingSessionIdError] 如果缺少 session
122
+ # @raise [FileNotExistError] 如果本地文件不存在
123
+ # @raise [InvalidFilePathError] 如果目标路径是非法文件路径
124
+ #
125
+ # @return [Hash]
126
+ def upload_slice(dst_path, src_path, options = {})
127
+ dst_path = fixed_path(dst_path)
128
+ fail FileNotExistError unless File.exist?(src_path)
129
+ bucket = validates(dst_path, options)
130
+
131
+ filesize = File.size(src_path)
132
+ sha = Utils.generate_file_sha(src_path)
133
+ sign = authorization.sign(bucket)
134
+
135
+ resp = init_slice_upload(dst_path, filesize, sha, options.merge('sign' => sign))
136
+ puts "init slice upload: #{resp}"
137
+
138
+ data = resp['data']
139
+ return data if data.key?('url') # 妙传命中
140
+
141
+ slice_size = data['slice_size'] || DEFAULT_SLICE_SIZE
142
+ session = data['session'] || options['session']
143
+ offset = data['offset'] || 0
144
+
145
+ fail MissingSessionIdError unless session
146
+
147
+ while offset < filesize
148
+ filecontent = IO.read(src_path, slice_size, offset)
149
+
150
+ retry_times = 0
151
+ begin
152
+ result = upload_part(dst_path, session, offset, filecontent, options)
153
+ puts "upload part data: #{result}"
154
+
155
+ if result.key?('data') && result['data'].key?('session')
156
+ session = result['data']['session']
157
+ elsif result.key?('data') && result['data'].key?('url')
158
+ return result['data']
159
+ end
160
+ rescue => e
161
+ retry_times += 1
162
+ puts "retry part upload request with offset:#{offset}, slice_size:#{slice_size} ..."
163
+ retry if retry_times <= config.max_retry_times
164
+ raise e
165
+ end
166
+ offset += slice_size
167
+ end
168
+ end
169
+
170
+ # 初始化分片上传
171
+ # @private
172
+ #
173
+ # @param path [String] 指定上传文件的路径
174
+ # @param filesize [Integer] 指定文件总大小
175
+ # @param sha [String] 指定该文件的 sha 值
176
+ # @param options [Hash] options
177
+ # @option options [String] :bucket (config.bucket_name) 指定当前 bucket, 默认是配置里面的 bucket
178
+ # @option options [Integer] :biz_attr 指定文件的 biz_attr 由业务端维护, 会在文件信息中返回
179
+ # @option options [Integer] :session 如果想要断点续传,则带上上一次的session
180
+ # @option options [Integer] :slice_size 指定分片大小
181
+ #
182
+ # @return [Hash]
183
+ def init_slice_upload(path, filesize, sha, options = {})
184
+ path = fixed_path(path)
185
+ bucket = validates(path, options)
186
+
187
+ url = generate_rest_url(bucket, path)
188
+ query = generate_slice_upload_query(filesize, sha, options)
189
+ sign = options['sign'] || authorization.sign(bucket)
190
+
191
+ http.post(url, query: query, headers: { 'Authorization' => sign }).parsed_response
192
+ end
193
+ private :init_slice_upload
194
+
195
+ # 上传分片数据
196
+ # @private
197
+ #
198
+ # @param path [String] 指定上传文件的路径
199
+ # @param session [String] 指定分片上传的 session id
200
+ # @param offset [Integer] 本次分片位移
201
+ # @param content [Binary] 指定文件内容
202
+ # @param options [Hash] options
203
+ # @option options [String] :bucket (config.bucket_name) 指定当前 bucket, 默认是配置里面的 bucket
204
+ #
205
+ # @return [Hash]
206
+ def upload_part(path, session, offset, content, options = {})
207
+ path = fixed_path(path)
208
+ bucket = validates(path, options)
209
+
210
+ url = generate_rest_url(bucket, path)
211
+ query = generate_upload_part_query(session, offset, content)
212
+ sign = options['sign'] || authorization.sign(bucket)
213
+
214
+ http.post(url, query: query, headers: { 'Authorization' => sign }).parsed_response
215
+ end
216
+ private :upload_part
217
+
218
+ # 更新文件或者目录信息
219
+ #
220
+ # @param path [String] 指定文件或者目录路径
221
+ # @param biz_attr [String] 指定文件或者目录的 biz_attr
222
+ # @param options [Hash] 额外参数
223
+ # @option options [String] :bucket (config.bucket_name) 指定当前 bucket, 默认是配置里面的 bucket
224
+ #
225
+ # @return [Hash]
226
+ def update(path, biz_attr, options = {})
227
+ path = fixed_path(path)
228
+ bucket = validates(path, options, 'both')
229
+ url = generate_rest_url(bucket, path)
230
+
231
+ query = { 'op' => 'update', 'biz_attr' => biz_attr }
232
+
233
+ resource = "/#{bucket}#{Utils.url_encode(path)}"
234
+ headers = {
235
+ 'Authorization' => authorization.sign_once(bucket, resource),
236
+ 'Content-Type' => 'application/json'
237
+ }
238
+
239
+ http.post(url, body: query.to_json, headers: headers).parsed_response
240
+ end
241
+
242
+ # 删除文件或者目录
243
+ #
244
+ # @param path [String] 指定文件或者目录路径
245
+ # @param options [Hash] 额外参数
246
+ # @option options [String] :bucket (config.bucket_name) 指定当前 bucket, 默认是配置里面的 bucket
247
+ #
248
+ # @return [Hash]
249
+ def delete(path, options = {})
250
+ path = fixed_path(path)
251
+ bucket = validates(path, options, 'both')
252
+ url = generate_rest_url(bucket, path)
253
+
254
+ query = { 'op' => 'delete' }
255
+
256
+ resource = "/#{bucket}#{Utils.url_encode(path)}"
257
+ headers = {
258
+ 'Authorization' => authorization.sign_once(bucket, resource),
259
+ 'Content-Type' => 'application/json'
260
+ }
261
+
262
+ http.post(url, body: query.to_json, headers: headers).parsed_response
263
+ end
264
+
265
+ # 删除目录
266
+ #
267
+ # @param path [String] 指定目录路径
268
+ # @param options [Hash] 额外参数
269
+ # @option options [String] :bucket (config.bucket_name) 指定当前 bucket, 默认是配置里面的 bucket
270
+ # @option options [Boolean] :recursive (false) 指定是否需要级连删除
271
+ #
272
+ # @raise [InvalidFolderPathError] 如果路径是非法文件夹路径
273
+ #
274
+ # @return [Hash]
275
+ def delete_folder(path, options = {})
276
+ validates(path, options, 'folder_only')
277
+
278
+ return delete(path, options) if options['recursive'] != true
279
+
280
+ loop do
281
+ objects = list(path, options)
282
+ objects.each do |object|
283
+ if object.is_a?(QcloudCos::FolderObject)
284
+ delete_folder("#{path}#{object.name}/", options)
285
+ elsif object.is_a?(QcloudCos::FileObject)
286
+ delete_file("#{path}#{object.name}", options)
287
+ end
288
+ end
289
+ break unless objects.has_more
290
+ options['context'] = objects.context
291
+ end
292
+ delete(path)
293
+ end
294
+
295
+ # 删除文件
296
+ #
297
+ # @param path [String] 指定文件路径
298
+ # @param options [Hash] 额外参数
299
+ # @option options [String] :bucket (config.bucket_name) 指定当前 bucket, 默认是配置里面的 bucket
300
+ #
301
+ # @raise [InvalidFilePathError] 如果文件路径不合法
302
+ #
303
+ # @return [Hash]
304
+ def delete_file(path, options = {})
305
+ fail InvalidFilePathError if path.end_with?('/')
306
+ delete(path, options)
307
+ end
308
+
309
+ # 查看文件或者文件夹信息
310
+ #
311
+ # @param path [String] 指定文件或者文件夹目录
312
+ # @param options [Hash] 额外参数
313
+ # @option options [String] :bucket (config.bucket_name) 指定当前 bucket, 默认是配置里面的 bucket
314
+ #
315
+ # @return [Hash]
316
+ def stat(path, options = {})
317
+ path = fixed_path(path)
318
+ bucket = validates(path, options, 'both')
319
+ url = generate_rest_url(bucket, path)
320
+
321
+ query = { 'op' => 'stat' }
322
+ sign = authorization.sign(bucket)
323
+
324
+ http.get(url, query: query, headers: { 'Authorization' => sign }).parsed_response
325
+ end
326
+
327
+ private
328
+
329
+ def generate_slice_upload_query(filesize, sha, options)
330
+ {
331
+ 'op' => 'upload_slice',
332
+ 'filesize' => filesize,
333
+ 'sha' => sha,
334
+ 'filecontent' => Tempfile.new("temp-#{Time.now.to_i}")
335
+ }.merge(Utils.hash_slice(options, 'biz_attr', 'session', 'slice_size'))
336
+ end
337
+
338
+ def generate_upload_part_query(session, offset, content)
339
+ {
340
+ 'op' => 'upload_slice',
341
+ 'session' => session,
342
+ 'offset' => offset
343
+ }.merge(generate_file_query(content))
344
+ end
345
+
346
+ def generate_file_query(file_or_bin)
347
+ query = {}
348
+ if file_or_bin.respond_to?(:read)
349
+ query['filecontent'] = file_or_bin
350
+ query['sha'] = Utils.generate_sha(IO.binread(file_or_bin))
351
+ else
352
+ query['filecontent'] = generate_tempfile(file_or_bin)
353
+ query['sha'] = Utils.generate_sha(file_or_bin)
354
+ end
355
+ query
356
+ end
357
+
358
+ def generate_tempfile(file_or_bin)
359
+ tempfile = Tempfile.new("temp-#{Time.now.to_i}")
360
+ tempfile.write(file_or_bin)
361
+ tempfile.rewind
362
+ tempfile
363
+ end
364
+ end
365
+ end