qiniu 6.0.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/.gitignore +18 -0
- data/.rspec +1 -0
- data/.travis.yml +14 -0
- data/CHANGELOG.md +83 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +37 -0
- data/LICENSE +22 -0
- data/README.md +45 -0
- data/Rakefile +21 -0
- data/docs/README.md +790 -0
- data/lib/qiniu-rs.rb +2 -0
- data/lib/qiniu.rb +229 -0
- data/lib/qiniu/abstract.rb +22 -0
- data/lib/qiniu/auth.rb +25 -0
- data/lib/qiniu/config.rb +61 -0
- data/lib/qiniu/exceptions.rb +120 -0
- data/lib/qiniu/fop.rb +3 -0
- data/lib/qiniu/image.rb +38 -0
- data/lib/qiniu/log.rb +15 -0
- data/lib/qiniu/management.rb +114 -0
- data/lib/qiniu/misc.rb +35 -0
- data/lib/qiniu/resumable_upload.rb +306 -0
- data/lib/qiniu/storage.rb +5 -0
- data/lib/qiniu/tokens/access_token.rb +21 -0
- data/lib/qiniu/tokens/download_token.rb +31 -0
- data/lib/qiniu/tokens/qbox_token.rb +38 -0
- data/lib/qiniu/tokens/upload_token.rb +47 -0
- data/lib/qiniu/upload.rb +114 -0
- data/lib/qiniu/utils.rb +151 -0
- data/lib/qiniu/version.rb +17 -0
- data/qiniu.gemspec +28 -0
- data/spec/qiniu/abstract_spec.rb +30 -0
- data/spec/qiniu/image_logo_for_test.png +0 -0
- data/spec/qiniu/image_spec.rb +87 -0
- data/spec/qiniu/management_spec.rb +181 -0
- data/spec/qiniu/misc_spec.rb +59 -0
- data/spec/qiniu/qiniu_spec.rb +385 -0
- data/spec/qiniu/tokens/qbox_token_spec.rb +29 -0
- data/spec/qiniu/upload_spec.rb +184 -0
- data/spec/qiniu/utils_spec.rb +49 -0
- data/spec/qiniu/version_spec.rb +10 -0
- data/spec/spec_helper.rb +11 -0
- metadata +194 -0
data/lib/qiniu-rs.rb
ADDED
data/lib/qiniu.rb
ADDED
@@ -0,0 +1,229 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module Qiniu
|
4
|
+
autoload :Version, 'qiniu/version'
|
5
|
+
autoload :Utils, 'qiniu/utils'
|
6
|
+
autoload :Auth, 'qiniu/auth'
|
7
|
+
autoload :Config, 'qiniu/config'
|
8
|
+
autoload :Log, 'qiniu/log'
|
9
|
+
autoload :Exception, 'qiniu/exceptions'
|
10
|
+
autoload :AccessToken, 'qiniu/tokens/access_token'
|
11
|
+
autoload :QboxToken, 'qiniu/tokens/qbox_token'
|
12
|
+
autoload :UploadToken, 'qiniu/tokens/upload_token'
|
13
|
+
autoload :DownloadToken, 'qiniu/tokens/download_token'
|
14
|
+
autoload :Abstract, 'qiniu/abstract'
|
15
|
+
autoload :Storage, 'qiniu/storage'
|
16
|
+
autoload :Fop, 'qiniu/fop'
|
17
|
+
autoload :Misc, 'qiniu/misc'
|
18
|
+
|
19
|
+
class << self
|
20
|
+
|
21
|
+
StatusOK = 200
|
22
|
+
|
23
|
+
def establish_connection!(opts = {})
|
24
|
+
Config.initialize_connect opts
|
25
|
+
end
|
26
|
+
|
27
|
+
def mkbucket(bucket_name)
|
28
|
+
code, data = Storage.mkbucket(bucket_name)
|
29
|
+
code == StatusOK
|
30
|
+
end
|
31
|
+
|
32
|
+
def buckets
|
33
|
+
code, data = Storage.buckets
|
34
|
+
code == StatusOK ? data : false
|
35
|
+
end
|
36
|
+
|
37
|
+
def set_protected(bucket, protected_mode)
|
38
|
+
code, data = Misc.set_protected(bucket, protected_mode)
|
39
|
+
code == StatusOK
|
40
|
+
end
|
41
|
+
|
42
|
+
def set_separator(bucket, separator)
|
43
|
+
code, data = Misc.set_separator(bucket, separator)
|
44
|
+
code == StatusOK
|
45
|
+
end
|
46
|
+
|
47
|
+
def set_style(bucket, name, style)
|
48
|
+
code, data = Misc.set_style(bucket, name, style)
|
49
|
+
code == StatusOK
|
50
|
+
end
|
51
|
+
|
52
|
+
def unset_style(bucket, name)
|
53
|
+
code, data = Misc.unset_style(bucket, name)
|
54
|
+
code == StatusOK
|
55
|
+
end
|
56
|
+
|
57
|
+
def put_file opts = {}
|
58
|
+
code, data = Storage.put_file(opts[:file],
|
59
|
+
opts[:bucket],
|
60
|
+
opts[:key],
|
61
|
+
opts[:mime_type],
|
62
|
+
opts[:note],
|
63
|
+
opts[:enable_crc32_check])
|
64
|
+
code == StatusOK
|
65
|
+
end
|
66
|
+
|
67
|
+
def upload_file opts = {}
|
68
|
+
uncontained_opts = [:uptoken, :file, :bucket, :key] - opts.keys
|
69
|
+
raise MissingArgsError, uncontained_opts unless uncontained_opts.empty?
|
70
|
+
|
71
|
+
source_file = opts[:file]
|
72
|
+
raise NoSuchFileError, source_file unless File.exist?(source_file)
|
73
|
+
|
74
|
+
opts[:enable_resumable_upload] = true unless opts.has_key?(:enable_resumable_upload)
|
75
|
+
|
76
|
+
if opts[:enable_resumable_upload] && File::size(source_file) > Config.settings[:block_size]
|
77
|
+
code, data = Storage.upload_with_token(opts[:uptoken],
|
78
|
+
opts[:file],
|
79
|
+
opts[:bucket],
|
80
|
+
opts[:key],
|
81
|
+
opts[:mime_type],
|
82
|
+
opts[:note],
|
83
|
+
opts[:customer],
|
84
|
+
opts[:callback_params],
|
85
|
+
opts[:rotate])
|
86
|
+
else
|
87
|
+
code, data = Storage.upload_with_token(opts[:uptoken],
|
88
|
+
opts[:file],
|
89
|
+
opts[:bucket],
|
90
|
+
opts[:key],
|
91
|
+
opts[:mime_type],
|
92
|
+
opts[:note],
|
93
|
+
opts[:callback_params],
|
94
|
+
opts[:enable_crc32_check],
|
95
|
+
opts[:rotate])
|
96
|
+
end
|
97
|
+
raise UploadFailedError.new(code, data) if code != StatusOK
|
98
|
+
return data
|
99
|
+
end
|
100
|
+
|
101
|
+
def stat(bucket, key)
|
102
|
+
code, data = Storage.stat(bucket, key)
|
103
|
+
code == StatusOK ? data : false
|
104
|
+
end
|
105
|
+
|
106
|
+
def get(bucket, key, save_as = nil, expires_in = nil, version = nil)
|
107
|
+
code, data = Storage.get(bucket, key, save_as, expires_in, version)
|
108
|
+
code == StatusOK ? data : false
|
109
|
+
end
|
110
|
+
|
111
|
+
def download(bucket, key, save_as = nil, expires_in = nil, version = nil)
|
112
|
+
code, data = Storage.get(bucket, key, save_as, expires_in, version)
|
113
|
+
code == StatusOK ? data["url"] : false
|
114
|
+
end
|
115
|
+
|
116
|
+
def copy(source_bucket, source_key, target_bucket, target_key)
|
117
|
+
code, data = Storage.copy(source_bucket, source_key, target_bucket, target_key)
|
118
|
+
code == StatusOK
|
119
|
+
end
|
120
|
+
|
121
|
+
def move(source_bucket, source_key, target_bucket, target_key)
|
122
|
+
code, data = Storage.move(source_bucket, source_key, target_bucket, target_key)
|
123
|
+
code == StatusOK
|
124
|
+
end
|
125
|
+
|
126
|
+
def delete(bucket, key)
|
127
|
+
code, data = Storage.delete(bucket, key)
|
128
|
+
code == StatusOK
|
129
|
+
end
|
130
|
+
|
131
|
+
def batch(command, bucket, keys)
|
132
|
+
code, data = Storage.batch(command, bucket, keys)
|
133
|
+
code == StatusOK ? data : false
|
134
|
+
end
|
135
|
+
|
136
|
+
def batch_stat(bucket, keys)
|
137
|
+
code, data = Storage.batch_stat(bucket, keys)
|
138
|
+
code == StatusOK ? data : false
|
139
|
+
end
|
140
|
+
|
141
|
+
def batch_get(bucket, keys)
|
142
|
+
code, data = Storage.batch_get(bucket, keys)
|
143
|
+
code == StatusOK ? data : false
|
144
|
+
end
|
145
|
+
|
146
|
+
def batch_copy(*args)
|
147
|
+
code, data = Storage.batch_copy(args)
|
148
|
+
code == StatusOK
|
149
|
+
end
|
150
|
+
|
151
|
+
def batch_move(*args)
|
152
|
+
code, data = Storage.batch_move(args)
|
153
|
+
code == StatusOK
|
154
|
+
end
|
155
|
+
|
156
|
+
def batch_download(bucket, keys)
|
157
|
+
code, data = Storage.batch_get(bucket, keys)
|
158
|
+
return false unless code == StatusOK
|
159
|
+
links = []
|
160
|
+
data.each { |e| links << e["data"]["url"] }
|
161
|
+
links
|
162
|
+
end
|
163
|
+
|
164
|
+
def batch_delete(bucket, keys)
|
165
|
+
code, data = Storage.batch_delete(bucket, keys)
|
166
|
+
code == StatusOK ? data : false
|
167
|
+
end
|
168
|
+
|
169
|
+
def publish(domain, bucket)
|
170
|
+
code, data = Storage.publish(domain, bucket)
|
171
|
+
code == StatusOK
|
172
|
+
end
|
173
|
+
|
174
|
+
def unpublish(domain)
|
175
|
+
code, data = Storage.unpublish(domain)
|
176
|
+
code == StatusOK
|
177
|
+
end
|
178
|
+
|
179
|
+
def drop(bucket)
|
180
|
+
code, data = Storage.drop(bucket)
|
181
|
+
code == StatusOK
|
182
|
+
end
|
183
|
+
|
184
|
+
def image_info(url)
|
185
|
+
code, data = Fop::Image.info(url)
|
186
|
+
code == StatusOK ? data : false
|
187
|
+
end
|
188
|
+
|
189
|
+
def image_exif(url)
|
190
|
+
code, data = Fop::Image.exif(url)
|
191
|
+
code == StatusOK ? data : false
|
192
|
+
end
|
193
|
+
|
194
|
+
def image_mogrify_preview_url(source_image_url, options)
|
195
|
+
Fop::Image.mogrify_preview_url(source_image_url, options)
|
196
|
+
end
|
197
|
+
|
198
|
+
def image_mogrify_save_as(bucket, key, source_image_url, options)
|
199
|
+
code, data = Storage.image_mogrify_save_as(bucket, key, source_image_url, options)
|
200
|
+
code == StatusOK ? data : false
|
201
|
+
end
|
202
|
+
|
203
|
+
def generate_upload_token(opts = {})
|
204
|
+
token_obj = UploadToken.new(opts)
|
205
|
+
token_obj.access_key = Config.settings[:access_key]
|
206
|
+
token_obj.secret_key = Config.settings[:secret_key]
|
207
|
+
#token_obj.scope = opts[:scope]
|
208
|
+
#token_obj.expires_in = opts[:expires_in]
|
209
|
+
#token_obj.callback_url = opts[:callback_url]
|
210
|
+
#token_obj.callback_body_type = opts[:callback_body_type]
|
211
|
+
#token_obj.customer = opts[:customer]
|
212
|
+
#token_obj.escape = opts[:escape]
|
213
|
+
#token_obj.async_options = opts[:async_options]
|
214
|
+
#token_obj.return_body = opts[:return_body]
|
215
|
+
token_obj.generate_token
|
216
|
+
end
|
217
|
+
|
218
|
+
def generate_download_token(opts = {})
|
219
|
+
token_obj = DownloadToken.new(opts)
|
220
|
+
token_obj.access_key = Config.settings[:access_key]
|
221
|
+
token_obj.secret_key = Config.settings[:secret_key]
|
222
|
+
#token_obj.expires_in = opts[:expires_in]
|
223
|
+
#token_obj.pattern = opts[:pattern]
|
224
|
+
token_obj.generate_token
|
225
|
+
end
|
226
|
+
|
227
|
+
end
|
228
|
+
|
229
|
+
end # module Qiniu
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module Qiniu
|
4
|
+
module Abstract
|
5
|
+
def self.included(base)
|
6
|
+
base.extend(ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def abstract_methods(*args)
|
11
|
+
args.each do |name|
|
12
|
+
class_eval <<-END
|
13
|
+
def #{name}(*args)
|
14
|
+
errmsg = %Q(class \#{self.class.name} must implement abstract method #{self.name}##{name}().)
|
15
|
+
raise NotImplementedError.new(errmsg)
|
16
|
+
end
|
17
|
+
END
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end # module Abstract
|
22
|
+
end # module Qiniu
|
data/lib/qiniu/auth.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'qiniu/exceptions'
|
4
|
+
|
5
|
+
module Qiniu
|
6
|
+
module Auth
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
include Utils
|
11
|
+
|
12
|
+
def call_with_signature(url, data, retry_times = 0, options = {})
|
13
|
+
code, data = http_request url, data, options.merge({:qbox_signature_token => generate_qbox_signature(url, data, options[:mime])})
|
14
|
+
[code, data]
|
15
|
+
end
|
16
|
+
|
17
|
+
def request(url, data = nil, options = {})
|
18
|
+
code, data = Auth.call_with_signature(url, data, 0, options)
|
19
|
+
[code, data]
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end # module Auth
|
25
|
+
end # module Qiniu
|
data/lib/qiniu/config.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# USAGE WAY 1:
|
4
|
+
# Qbox::Config.initialize_connect :client_id => "<ClientID>",
|
5
|
+
# :client_secret => "<ClientSecret>"
|
6
|
+
#
|
7
|
+
# USAGE WAY 2:
|
8
|
+
# Qbox::Config.load "path/to/your_project/config/qiniu.yml"
|
9
|
+
#
|
10
|
+
|
11
|
+
require 'tmpdir'
|
12
|
+
|
13
|
+
module Qiniu
|
14
|
+
module Config
|
15
|
+
class << self
|
16
|
+
|
17
|
+
DEFAULT_OPTIONS = {
|
18
|
+
:user_agent => 'Qiniu-RS-Ruby-SDK-' + Version.to_s + '()',
|
19
|
+
:method => :post,
|
20
|
+
:content_type => 'application/x-www-form-urlencoded',
|
21
|
+
:auth_url => "https://acc.qbox.me/oauth2/token",
|
22
|
+
:rs_host => "http://rs.qiniu.com",
|
23
|
+
:io_host => "http://iovip.qbox.me",
|
24
|
+
:up_host => "http://up.qiniu.com",
|
25
|
+
:pub_host => "http://pu.qbox.me:10200",
|
26
|
+
:eu_host => "http://eu.qbox.me",
|
27
|
+
:client_id => "a75604760c4da4caaa456c0c5895c061c3065c5a",
|
28
|
+
:client_secret => "75df554a39f58accb7eb293b550fa59618674b7d",
|
29
|
+
:access_key => "",
|
30
|
+
:secret_key => "",
|
31
|
+
:auto_reconnect => true,
|
32
|
+
:max_retry_times => 3,
|
33
|
+
:block_size => 1024*1024*4,
|
34
|
+
:chunk_size => 1024*256,
|
35
|
+
:enable_debug => true,
|
36
|
+
:tmpdir => Dir.tmpdir + File::SEPARATOR + 'Qiniu-RS-Ruby-SDK'
|
37
|
+
}
|
38
|
+
|
39
|
+
REQUIRED_OPTION_KEYS = [:access_key, :secret_key]
|
40
|
+
|
41
|
+
attr_reader :settings, :default_params
|
42
|
+
|
43
|
+
def load config_file
|
44
|
+
if File.exist?(config_file)
|
45
|
+
config_options = YAML.load_file(config_file)
|
46
|
+
initialize_connect(config_options)
|
47
|
+
else
|
48
|
+
raise MissingConfError, config_file
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def initialize_connect options = {}
|
53
|
+
@settings = DEFAULT_OPTIONS.merge(options)
|
54
|
+
REQUIRED_OPTION_KEYS.each do |opt|
|
55
|
+
raise MissingArgsError, [opt] unless @settings.has_key?(opt)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end # module Config
|
61
|
+
end # module Qiniu
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module Qiniu
|
4
|
+
|
5
|
+
class Exception < RuntimeError
|
6
|
+
end
|
7
|
+
|
8
|
+
class ResponseError < Exception
|
9
|
+
attr_reader :response
|
10
|
+
|
11
|
+
def initialize(message, response = nil)
|
12
|
+
@response = response
|
13
|
+
super(message)
|
14
|
+
end
|
15
|
+
|
16
|
+
def http_code
|
17
|
+
@response.code.to_i if @response
|
18
|
+
end
|
19
|
+
|
20
|
+
def http_body
|
21
|
+
@response.body if @response
|
22
|
+
end
|
23
|
+
|
24
|
+
def inspect
|
25
|
+
"#{message}: #{http_body}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class RequestFailed < ResponseError
|
30
|
+
def message
|
31
|
+
"HTTP status code: #{http_code}. Response body: #{http_body}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
message
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class UploadFailedError < Exception
|
40
|
+
def initialize(status_code, response_data)
|
41
|
+
data_string = response_data.map { |key, value| %Q(:#{key.to_s} => #{value.to_s}) }
|
42
|
+
msg = %Q(Uploading Failed. HTTP Status Code: #{status_code}. HTTP response body: #{data_string.join(', ')}.)
|
43
|
+
super(msg)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class FileSeekReadError < Exception
|
48
|
+
def initialize(fpath, block_index, seek_pos, read_length, result_length)
|
49
|
+
msg = "Reading file: #{fpath}, "
|
50
|
+
msg += "at block index: #{block_index}. "
|
51
|
+
msg += "Expected seek_pos:#{seek_pos} and read_length:#{read_length}, "
|
52
|
+
msg += "but got result_length: #{result_length}."
|
53
|
+
super(msg)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class BlockSizeNotMathchError < Exception
|
58
|
+
def initialize(fpath, block_index, offset, restsize, block_size)
|
59
|
+
msg = "Reading file: #{fpath}, "
|
60
|
+
msg += "at block index: #{block_index}. "
|
61
|
+
msg += "Expected offset: #{offset}, restsize: #{restsize} and block_size: #{block_size}, "
|
62
|
+
msg += "but got offset+restsize=#{offset+restsize}."
|
63
|
+
super(msg)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class BlockCountNotMathchError < Exception
|
68
|
+
def initialize(fpath, block_count, checksum_count, progress_count)
|
69
|
+
msg = "Reading file: #{fpath}, "
|
70
|
+
msg += "Expected block_count, checksum_count, progress_count is: #{block_count}, "
|
71
|
+
msg += "but got checksum_count: #{checksum_count}, progress_count: #{progress_count}."
|
72
|
+
super(msg)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class MissingArgsError < Exception
|
77
|
+
def initialize(missing_keys)
|
78
|
+
key_list = missing_keys.map {|key| key.to_s}.join(' and the ')
|
79
|
+
super("You did not provide both required args. Please provide the #{key_list}.")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class MissingAccessToken < MissingArgsError
|
84
|
+
def initialize
|
85
|
+
super([:access_token])
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
class MissingRefreshToken < MissingArgsError
|
90
|
+
def initialize
|
91
|
+
super([:refresh_token])
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class MissingUsernameOrPassword < MissingArgsError
|
96
|
+
def initialize
|
97
|
+
super([:username, :password])
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class InvalidArgsError < Exception
|
102
|
+
def initialize(invalid_keys)
|
103
|
+
key_list = invalid_keys.map {|key| key.to_s}.join(' and the ')
|
104
|
+
super("#{key_list} should not be empty.")
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
class MissingConfError < Exception
|
109
|
+
def initialize(missing_conf_file)
|
110
|
+
super("Error, missing #{missing_conf_file}. You must have #{missing_conf_file} to configure your client id and secret.")
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
class NoSuchFileError < Exception
|
115
|
+
def initialize(missing_file)
|
116
|
+
super("Error, no such file #{missing_file}.")
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
end # module Qiniu
|