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