qiniu 6.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|