qiniu_jxb_fix 6.4.2
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.
- data/.gitignore +18 -0
- data/.rspec +1 -0
- data/.travis.yml +12 -0
- data/CHANGELOG.md +146 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +44 -0
- data/LICENSE +22 -0
- data/README.md +47 -0
- data/Rakefile +21 -0
- data/docs/README.md +790 -0
- data/lib/qiniu/abstract.rb +22 -0
- data/lib/qiniu/adt.rb +46 -0
- data/lib/qiniu/auth.rb +255 -0
- data/lib/qiniu/config.rb +60 -0
- data/lib/qiniu/exceptions.rb +120 -0
- data/lib/qiniu/fop.rb +4 -0
- data/lib/qiniu/http.rb +137 -0
- data/lib/qiniu/image.rb +38 -0
- data/lib/qiniu/log.rb +15 -0
- data/lib/qiniu/management.rb +166 -0
- data/lib/qiniu/misc.rb +35 -0
- data/lib/qiniu/pfop.rb +124 -0
- data/lib/qiniu/resumable_upload.rb +319 -0
- data/lib/qiniu/storage.rb +5 -0
- data/lib/qiniu/tokens/access_token.rb +23 -0
- data/lib/qiniu/tokens/download_token.rb +36 -0
- data/lib/qiniu/tokens/qbox_token.rb +38 -0
- data/lib/qiniu/tokens/upload_token.rb +57 -0
- data/lib/qiniu/upload.rb +134 -0
- data/lib/qiniu/utils.rb +109 -0
- data/lib/qiniu/version.rb +17 -0
- data/lib/qiniu-rs.rb +2 -0
- data/lib/qiniu.rb +209 -0
- data/qiniu.gemspec +29 -0
- data/rails3/Gemfile +4 -0
- data/rails3/qiniu.gemspec +29 -0
- data/spec/qiniu/abstract_spec.rb +30 -0
- data/spec/qiniu/auth_spec.rb +74 -0
- data/spec/qiniu/image_logo_for_test.png +0 -0
- data/spec/qiniu/image_spec.rb +80 -0
- data/spec/qiniu/management_spec.rb +163 -0
- data/spec/qiniu/misc_spec.rb +53 -0
- data/spec/qiniu/pfop_spec.rb +80 -0
- data/spec/qiniu/qiniu_spec.rb +321 -0
- data/spec/qiniu/tokens/qbox_token_spec.rb +29 -0
- data/spec/qiniu/upload_spec.rb +301 -0
- data/spec/qiniu/utils_spec.rb +49 -0
- data/spec/qiniu/version_spec.rb +10 -0
- data/spec/spec_helper.rb +19 -0
- metadata +222 -0
@@ -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/adt.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
# vim: sw=2 ts=2
|
3
|
+
|
4
|
+
require 'uri'
|
5
|
+
|
6
|
+
module Qiniu
|
7
|
+
module ADT
|
8
|
+
|
9
|
+
class ApiSpecification
|
10
|
+
public
|
11
|
+
def to_str; return ""; end
|
12
|
+
end # class ApiSpecification
|
13
|
+
|
14
|
+
module Policy
|
15
|
+
public
|
16
|
+
def to_json
|
17
|
+
args = {}
|
18
|
+
|
19
|
+
self.params.each_pair do |key, fld|
|
20
|
+
val = self.__send__(key)
|
21
|
+
if !val.nil? then
|
22
|
+
args[fld] = val
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
return args.to_json
|
27
|
+
end # to_json
|
28
|
+
|
29
|
+
def to_query_string
|
30
|
+
args = []
|
31
|
+
|
32
|
+
self.params.each_pair do |key, fld|
|
33
|
+
val = self.__send__(key)
|
34
|
+
if !val.nil? then
|
35
|
+
new_fld = CGI.escape(fld.to_s)
|
36
|
+
new_val = CGI.escape(val.to_s).gsub('+', '%20')
|
37
|
+
args.push("#{new_fld}=#{new_val}")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
return args.join("&")
|
42
|
+
end # to_query_string
|
43
|
+
end # module Policy
|
44
|
+
|
45
|
+
end # module ADT
|
46
|
+
end # module Qiniu
|
data/lib/qiniu/auth.rb
ADDED
@@ -0,0 +1,255 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
# vim: sw=2 ts=2
|
3
|
+
|
4
|
+
require 'hmac-sha1'
|
5
|
+
require 'uri'
|
6
|
+
require 'cgi'
|
7
|
+
|
8
|
+
require 'qiniu/exceptions'
|
9
|
+
require 'qiniu/helper'
|
10
|
+
|
11
|
+
module Qiniu
|
12
|
+
module Auth
|
13
|
+
extend Qiniu::Helper
|
14
|
+
DEFAULT_AUTH_SECONDS = 3600
|
15
|
+
|
16
|
+
class << self
|
17
|
+
def calculate_deadline(expires_in, deadline = nil)
|
18
|
+
### 授权期计算
|
19
|
+
if expires_in.is_a?(Integer) && expires_in > 0 then
|
20
|
+
# 指定相对时间,单位:秒
|
21
|
+
return get_expire_time.to_i + expires_in
|
22
|
+
elsif deadline.is_a?(Integer) then
|
23
|
+
# 指定绝对时间,常用于调试和单元测试
|
24
|
+
return deadline
|
25
|
+
end
|
26
|
+
|
27
|
+
# 默认授权期1小时
|
28
|
+
return get_expire_time.to_i + DEFAULT_AUTH_SECONDS
|
29
|
+
end # calculate_deadline
|
30
|
+
end # class << self
|
31
|
+
|
32
|
+
class PutPolicy
|
33
|
+
private
|
34
|
+
def initialize(bucket,
|
35
|
+
key = nil,
|
36
|
+
expires_in = DEFAULT_AUTH_SECONDS,
|
37
|
+
deadline = nil)
|
38
|
+
### 设定scope参数(必填项目)
|
39
|
+
self.scope!(bucket, key)
|
40
|
+
|
41
|
+
### 设定deadline参数(必填项目)
|
42
|
+
@expires_in = expires_in
|
43
|
+
@deadline = Auth.calculate_deadline(expires_in, deadline)
|
44
|
+
end # initialize
|
45
|
+
|
46
|
+
PARAMS = {
|
47
|
+
# 字符串类型参数
|
48
|
+
:scope => "scope" ,
|
49
|
+
:save_key => "saveKey" ,
|
50
|
+
:end_user => "endUser" ,
|
51
|
+
:return_url => "returnUrl" ,
|
52
|
+
:return_body => "returnBody" ,
|
53
|
+
:callback_url => "callbackUrl" ,
|
54
|
+
:callback_host => "callbackHost" ,
|
55
|
+
:callback_body => "callbackBody" ,
|
56
|
+
:callback_body_type => "callbackBodyType" ,
|
57
|
+
:persistent_ops => "persistentOps" ,
|
58
|
+
:persistent_notify_url => "persistentNotifyUrl" ,
|
59
|
+
:persistent_pipeline => "persistentPipeline" ,
|
60
|
+
|
61
|
+
# 数值类型参数
|
62
|
+
:deadline => "deadline" ,
|
63
|
+
:insert_only => "insertOnly" ,
|
64
|
+
:fsize_limit => "fsizeLimit" ,
|
65
|
+
:callback_fetch_key => "callbackFetchKey" ,
|
66
|
+
:detect_mime => "detectMime" ,
|
67
|
+
:mime_limit => "mimeLimit"
|
68
|
+
} # PARAMS
|
69
|
+
|
70
|
+
public
|
71
|
+
attr_reader :bucket, :key
|
72
|
+
|
73
|
+
def scope!(bucket, key = nil)
|
74
|
+
@bucket = bucket
|
75
|
+
@key = key
|
76
|
+
|
77
|
+
if key.nil? then
|
78
|
+
# 新增语义,文件已存在则失败
|
79
|
+
@scope = bucket
|
80
|
+
else
|
81
|
+
# 覆盖语义,文件已存在则直接覆盖
|
82
|
+
@scope = "#{bucket}:#{key}"
|
83
|
+
end
|
84
|
+
end # scope!
|
85
|
+
|
86
|
+
def expires_in!(seconds)
|
87
|
+
if !seconds.nil? then
|
88
|
+
return @expires_in
|
89
|
+
end
|
90
|
+
|
91
|
+
@epires_in = seconds
|
92
|
+
@deadline = Auth.calculate_deadline(seconds)
|
93
|
+
|
94
|
+
return @expires_in
|
95
|
+
end # expires_in!
|
96
|
+
|
97
|
+
def expires_in=(seconds)
|
98
|
+
return expires_in!(seconds)
|
99
|
+
end # expires_in=
|
100
|
+
|
101
|
+
def expires_in
|
102
|
+
return @expires_in
|
103
|
+
end # expires_in
|
104
|
+
|
105
|
+
def allow_mime_list! (list)
|
106
|
+
@mime_limit = list
|
107
|
+
end # allow_mime_list!
|
108
|
+
|
109
|
+
def deny_mime_list! (list)
|
110
|
+
@mime_limit = "!#{list}"
|
111
|
+
end # deny_mime_list!
|
112
|
+
|
113
|
+
def insert_only!
|
114
|
+
@insert_only = 1
|
115
|
+
end # insert_only!
|
116
|
+
|
117
|
+
def detect_mime!
|
118
|
+
@detect_mime = 1
|
119
|
+
end # detect_mime!
|
120
|
+
|
121
|
+
def to_json
|
122
|
+
args = {}
|
123
|
+
|
124
|
+
PARAMS.each_pair do |key, fld|
|
125
|
+
val = self.__send__(key)
|
126
|
+
if !val.nil? then
|
127
|
+
args[fld] = val
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
return args.to_json
|
132
|
+
end # to_json
|
133
|
+
|
134
|
+
PARAMS.each_pair do |key, fld|
|
135
|
+
attr_accessor key
|
136
|
+
end
|
137
|
+
end # class PutPolicy
|
138
|
+
|
139
|
+
class << self
|
140
|
+
EMPTY_ARGS = {}
|
141
|
+
|
142
|
+
### 生成下载授权URL
|
143
|
+
def authorize_download_url(url, args = EMPTY_ARGS)
|
144
|
+
### 提取AK/SK信息
|
145
|
+
access_key = Config.settings[:access_key]
|
146
|
+
secret_key = Config.settings[:secret_key]
|
147
|
+
|
148
|
+
download_url = url
|
149
|
+
|
150
|
+
### URL变换:追加FOP指令
|
151
|
+
if args[:fop].is_a?(String) && args[:fop] != '' then
|
152
|
+
if download_url.index('?').is_a?(Fixnum) then
|
153
|
+
# 已有参数
|
154
|
+
download_url = "#{download_url}&#{args[:fop]}"
|
155
|
+
else
|
156
|
+
# 尚无参数
|
157
|
+
download_url = "#{download_url}?#{args[:fop]}"
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
### 授权期计算
|
162
|
+
e = Auth.calculate_deadline(args[:expires_in], args[:deadline])
|
163
|
+
|
164
|
+
### URL变换:追加授权期参数
|
165
|
+
if download_url.index('?').is_a?(Fixnum) then
|
166
|
+
# 已有参数
|
167
|
+
download_url = "#{download_url}&e=#{e}"
|
168
|
+
else
|
169
|
+
# 尚无参数
|
170
|
+
download_url = "#{download_url}?e=#{e}"
|
171
|
+
end
|
172
|
+
|
173
|
+
### 生成数字签名
|
174
|
+
sign = HMAC::SHA1.new(secret_key).update(download_url).digest
|
175
|
+
encoded_sign = Utils.urlsafe_base64_encode(sign)
|
176
|
+
|
177
|
+
### 生成下载授权凭证
|
178
|
+
dntoken = "#{access_key}:#{encoded_sign}"
|
179
|
+
|
180
|
+
### 返回下载授权URL
|
181
|
+
return "#{download_url}&token=#{dntoken}"
|
182
|
+
end # authorize_download_url
|
183
|
+
|
184
|
+
### 对包含中文或其它 utf-8 字符的 Key 做下载授权
|
185
|
+
def authorize_download_url_2(domain, key, args = EMPTY_ARGS)
|
186
|
+
url_encoded_key = CGI::escape(key)
|
187
|
+
|
188
|
+
schema = args[:schema] || "http"
|
189
|
+
port = args[:port]
|
190
|
+
|
191
|
+
if port.nil? then
|
192
|
+
download_url = "#{schema}://#{domain}/#{url_encoded_key}"
|
193
|
+
else
|
194
|
+
download_url = "#{schema}://#{domain}:#{port}/#{url_encoded_key}"
|
195
|
+
end
|
196
|
+
return authorize_download_url(download_url, args)
|
197
|
+
end # authorize_download_url_2
|
198
|
+
|
199
|
+
def generate_acctoken(url, body = '')
|
200
|
+
### 提取AK/SK信息
|
201
|
+
access_key = Config.settings[:access_key]
|
202
|
+
secret_key = Config.settings[:secret_key]
|
203
|
+
|
204
|
+
### 解析URL,生成待签名字符串
|
205
|
+
uri = URI.parse(url)
|
206
|
+
signing_str = uri.path
|
207
|
+
|
208
|
+
# 如有QueryString部分,则需要加上
|
209
|
+
query_string = uri.query
|
210
|
+
if query_string.is_a?(String) && !query_string.empty?
|
211
|
+
signing_str += '?' + query_string
|
212
|
+
end
|
213
|
+
|
214
|
+
# 追加换行符
|
215
|
+
signing_str += "\n"
|
216
|
+
|
217
|
+
# 如果有Body,则也加上
|
218
|
+
# (仅限于mime == "application/x-www-form-urlencoded"的情况)
|
219
|
+
if body.is_a?(String) && !body.empty?
|
220
|
+
signing_str += body
|
221
|
+
end
|
222
|
+
|
223
|
+
### 生成数字签名
|
224
|
+
sign = HMAC::SHA1.new(secret_key).update(signing_str).digest
|
225
|
+
encoded_sign = Utils.urlsafe_base64_encode(sign)
|
226
|
+
|
227
|
+
### 生成管理授权凭证
|
228
|
+
acctoken = "#{access_key}:#{encoded_sign}"
|
229
|
+
|
230
|
+
### 返回管理授权凭证
|
231
|
+
return acctoken
|
232
|
+
end # generate_acctoken
|
233
|
+
|
234
|
+
def generate_uptoken(put_policy)
|
235
|
+
### 提取AK/SK信息
|
236
|
+
access_key = Config.settings[:access_key]
|
237
|
+
secret_key = Config.settings[:secret_key]
|
238
|
+
|
239
|
+
### 生成待签名字符串
|
240
|
+
encoded_put_policy = Utils.urlsafe_base64_encode(put_policy.to_json)
|
241
|
+
|
242
|
+
### 生成数字签名
|
243
|
+
sign = HMAC::SHA1.new(secret_key).update(encoded_put_policy).digest
|
244
|
+
encoded_sign = Utils.urlsafe_base64_encode(sign)
|
245
|
+
|
246
|
+
### 生成上传授权凭证
|
247
|
+
uptoken = "#{access_key}:#{encoded_sign}:#{encoded_put_policy}"
|
248
|
+
|
249
|
+
### 返回上传授权凭证
|
250
|
+
return uptoken
|
251
|
+
end # generate_uptoken
|
252
|
+
end # class << self
|
253
|
+
|
254
|
+
end # module Auth
|
255
|
+
end # module Qiniu
|
data/lib/qiniu/config.rb
ADDED
@@ -0,0 +1,60 @@
|
|
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 => 'QiniuRuby/' + Version.to_s + ' ('+RUBY_PLATFORM+')' + ' Ruby/'+ RUBY_VERSION,
|
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
|
+
:rsf_host => "http://rsf.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
|
+
:access_key => "",
|
28
|
+
:secret_key => "",
|
29
|
+
:auto_reconnect => true,
|
30
|
+
:max_retry_times => 3,
|
31
|
+
:block_size => 1024*1024*4,
|
32
|
+
:chunk_size => 1024*256,
|
33
|
+
:enable_debug => true,
|
34
|
+
:tmpdir => Dir.tmpdir + File::SEPARATOR + 'QiniuRuby'
|
35
|
+
}
|
36
|
+
|
37
|
+
REQUIRED_OPTION_KEYS = [:access_key, :secret_key, :up_host]
|
38
|
+
|
39
|
+
attr_reader :settings, :default_params
|
40
|
+
|
41
|
+
def load config_file
|
42
|
+
if File.exist?(config_file)
|
43
|
+
config_options = YAML.load_file(config_file)
|
44
|
+
config_options.symbolize_keys!
|
45
|
+
initialize_connect(config_options)
|
46
|
+
else
|
47
|
+
raise MissingConfError, config_file
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def initialize_connect options = {}
|
52
|
+
@settings = DEFAULT_OPTIONS.merge!(options)
|
53
|
+
REQUIRED_OPTION_KEYS.each do |opt|
|
54
|
+
raise MissingArgsError, [opt] unless @settings.has_key?(opt)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end # module Config
|
60
|
+
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
|
data/lib/qiniu/fop.rb
ADDED
data/lib/qiniu/http.rb
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
# vim: sw=2 ts=2
|
3
|
+
|
4
|
+
module Qiniu
|
5
|
+
module HTTP
|
6
|
+
|
7
|
+
class << self
|
8
|
+
public
|
9
|
+
def is_response_ok?(http_code)
|
10
|
+
return 200 <= http_code && http_code <= 299
|
11
|
+
end # is_response_ok?
|
12
|
+
|
13
|
+
def generate_query_string(params)
|
14
|
+
if params.is_a?(Hash)
|
15
|
+
total_param = params.map { |key, value| %Q(#{CGI.escape(key.to_s)}=#{CGI.escape(value.to_s).gsub('+', '%20')}) }
|
16
|
+
return total_param.join("&")
|
17
|
+
end
|
18
|
+
|
19
|
+
return params
|
20
|
+
end # generate_query_string
|
21
|
+
|
22
|
+
def get (url, opts = {})
|
23
|
+
### 配置请求Header
|
24
|
+
req_headers = {
|
25
|
+
:connection => 'close',
|
26
|
+
:accept => '*/*',
|
27
|
+
:user_agent => Config.settings[:user_agent]
|
28
|
+
}
|
29
|
+
|
30
|
+
# 优先使用外部Header,覆盖任何特定Header
|
31
|
+
if opts[:headers].is_a?(Hash) then
|
32
|
+
req_headers.merge!(opts[:headers])
|
33
|
+
end
|
34
|
+
|
35
|
+
### 发送请求
|
36
|
+
response = RestClient.get(url, req_headers)
|
37
|
+
return response.code.to_i, response.body, response.raw_headers
|
38
|
+
rescue => e
|
39
|
+
Log.logger.warn "#{e.message} => Qiniu::HTTP.get('#{url}')"
|
40
|
+
if e.respond_to?(:response) && e.response.respond_to?(:code) then
|
41
|
+
return e.response.code, e.response.body, e.response.raw_headers
|
42
|
+
end
|
43
|
+
return nil, nil, nil
|
44
|
+
end # get
|
45
|
+
|
46
|
+
API_RESULT_MIMETYPE = 'application/json'
|
47
|
+
|
48
|
+
def api_get (url, opts = {})
|
49
|
+
### 配置请求Header
|
50
|
+
headers = {
|
51
|
+
:accept => API_RESULT_MIMETYPE
|
52
|
+
}
|
53
|
+
|
54
|
+
# 将特定Header混入外部Header中
|
55
|
+
if opts[:headers].is_a?(Hash) then
|
56
|
+
opts[:headers] = opts[:headers].dup.merge!(headers)
|
57
|
+
else
|
58
|
+
opts[:headers] = headers
|
59
|
+
end
|
60
|
+
|
61
|
+
### 发送请求,然后转换返回值
|
62
|
+
resp_code, resp_body, resp_headers = get(url, opts)
|
63
|
+
if resp_code.nil? then
|
64
|
+
return 0, {}, {}
|
65
|
+
end
|
66
|
+
|
67
|
+
content_type = resp_headers["content-type"][0]
|
68
|
+
if !content_type.nil? && content_type == API_RESULT_MIMETYPE then
|
69
|
+
# 如果是JSON格式,则反序列化
|
70
|
+
resp_body = Utils.safe_json_parse(resp_body)
|
71
|
+
end
|
72
|
+
|
73
|
+
return resp_code, resp_body, resp_headers
|
74
|
+
end # api_get
|
75
|
+
|
76
|
+
def post (url, req_body = nil, opts = {})
|
77
|
+
### 配置请求Header
|
78
|
+
req_headers = {
|
79
|
+
:connection => 'close',
|
80
|
+
:accept => '*/*',
|
81
|
+
:user_agent => Config.settings[:user_agent]
|
82
|
+
}
|
83
|
+
|
84
|
+
# 优先使用外部Header,覆盖任何特定Header
|
85
|
+
if opts[:headers].is_a?(Hash) then
|
86
|
+
req_headers.merge!(opts[:headers])
|
87
|
+
end
|
88
|
+
|
89
|
+
### 发送请求
|
90
|
+
response = RestClient.post(url, req_body, req_headers)
|
91
|
+
return response.code.to_i, response.body, response.raw_headers
|
92
|
+
rescue => e
|
93
|
+
Log.logger.warn "#{e.message} => Qiniu::HTTP.post('#{url}')"
|
94
|
+
if e.respond_to?(:response) && e.response.respond_to?(:code) then
|
95
|
+
return e.response.code, e.response.body, e.response.raw_headers
|
96
|
+
end
|
97
|
+
return nil, nil, nil
|
98
|
+
end # post
|
99
|
+
|
100
|
+
def api_post (url, req_body = nil, opts = {})
|
101
|
+
### 配置请求Header
|
102
|
+
headers = {
|
103
|
+
:accept => API_RESULT_MIMETYPE
|
104
|
+
}
|
105
|
+
|
106
|
+
# 将特定Header混入外部Header中
|
107
|
+
if opts[:headers].is_a?(Hash) then
|
108
|
+
opts[:headers] = opts[:headers].dup.merge!(headers)
|
109
|
+
else
|
110
|
+
opts[:headers] = headers
|
111
|
+
end
|
112
|
+
|
113
|
+
### 发送请求,然后转换返回值
|
114
|
+
resp_code, resp_body, resp_headers = post(url, req_body, opts)
|
115
|
+
if resp_code.nil? then
|
116
|
+
return 0, {}, {}
|
117
|
+
end
|
118
|
+
|
119
|
+
content_type = resp_headers["content-type"][0]
|
120
|
+
if !content_type.nil? && content_type == API_RESULT_MIMETYPE then
|
121
|
+
# 如果是JSON格式,则反序列化
|
122
|
+
resp_body = Utils.safe_json_parse(resp_body)
|
123
|
+
end
|
124
|
+
|
125
|
+
return resp_code, resp_body, resp_headers
|
126
|
+
end # api_post
|
127
|
+
|
128
|
+
def management_post (url, body = '')
|
129
|
+
### 授权并执行管理操作
|
130
|
+
return HTTP.api_post(url, body, {
|
131
|
+
:headers => { 'Authorization' => 'QBox ' + Auth.generate_acctoken(url, body) }
|
132
|
+
})
|
133
|
+
end # management_post
|
134
|
+
end # class << self
|
135
|
+
|
136
|
+
end # module HTTP
|
137
|
+
end # module Qiniu
|
data/lib/qiniu/image.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module Qiniu
|
4
|
+
module Fop
|
5
|
+
module Image
|
6
|
+
class << self
|
7
|
+
include Utils
|
8
|
+
|
9
|
+
def info(url)
|
10
|
+
return HTTP.api_get(url + '?imageInfo')
|
11
|
+
end # info
|
12
|
+
|
13
|
+
def exif(url)
|
14
|
+
return HTTP.api_get(url + '?exif')
|
15
|
+
end # exif
|
16
|
+
|
17
|
+
def mogrify_preview_url(source_image_url, options)
|
18
|
+
source_image_url + '?' + generate_mogrify_params_string(options)
|
19
|
+
end
|
20
|
+
|
21
|
+
def generate_mogrify_params_string(options = {})
|
22
|
+
opts = {}
|
23
|
+
options.each do |k, v|
|
24
|
+
opts[k.to_s] = v
|
25
|
+
end
|
26
|
+
params_string = ""
|
27
|
+
keys = ["thumbnail", "gravity", "crop", "quality", "rotate", "format"]
|
28
|
+
keys.each do |key|
|
29
|
+
params_string += %Q(/#{key}/#{opts[key]}) unless opts[key].nil?
|
30
|
+
end
|
31
|
+
params_string += '/auto-orient' unless opts["auto_orient"].nil?
|
32
|
+
'imageMogr' + URI.escape(params_string)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end # module Image
|
37
|
+
end # module Fop
|
38
|
+
end # module Qiniu
|