qiniu 6.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'qiniu/image'
@@ -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
+ Utils.http_request url + '?imageInfo', nil, {:method => :get}
11
+ end # info
12
+
13
+ def exif(url)
14
+ Utils.http_request url + '?exif', nil, {:method => :get}
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
@@ -0,0 +1,15 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'logger'
4
+
5
+ module Qiniu
6
+ module Log
7
+ class << self
8
+ attr_accessor :logger
9
+
10
+ def logger
11
+ @logger ||= Logger.new(STDERR)
12
+ end
13
+ end
14
+ end # module Log
15
+ end # module Qiniu
@@ -0,0 +1,114 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module Qiniu
4
+ module Storage
5
+ class << self
6
+ include Utils
7
+
8
+ def buckets
9
+ Auth.request Config.settings[:rs_host] + '/buckets'
10
+ end # buckets
11
+
12
+ def mkbucket(bucket_name)
13
+ Auth.request Config.settings[:rs_host] + '/mkbucket/' + bucket_name
14
+ end # mkbucket
15
+
16
+ def stat(bucket, key)
17
+ Auth.request Config.settings[:rs_host] + '/stat/' + encode_entry_uri(bucket, key)
18
+ end # stat
19
+
20
+ def get(bucket, key, save_as = nil, expires_in = nil, version = nil)
21
+ url = Config.settings[:rs_host] + '/get/' + encode_entry_uri(bucket, key)
22
+ url += '/base/' + version unless version.nil?
23
+ url += '/attName/' + Utils.urlsafe_base64_encode(save_as) unless save_as.nil?
24
+ url += '/expires/' + expires_in.to_s if !expires_in.nil? && expires_in > 0
25
+ Auth.request url
26
+ end # get
27
+
28
+ def copy(source_bucket, source_key, target_bucket, target_key)
29
+ uri = _generate_cp_or_mv_opstr('copy', source_bucket, source_key, target_bucket, target_key)
30
+ Auth.request Config.settings[:rs_host] + uri
31
+ end # copy
32
+
33
+ def move(source_bucket, source_key, target_bucket, target_key)
34
+ uri = _generate_cp_or_mv_opstr('move', source_bucket, source_key, target_bucket, target_key)
35
+ Auth.request Config.settings[:rs_host] + uri
36
+ end # move
37
+
38
+ def delete(bucket, key)
39
+ Auth.request Config.settings[:rs_host] + '/delete/' + encode_entry_uri(bucket, key)
40
+ end # delete
41
+
42
+ def publish(domain, bucket)
43
+ encoded_domain = Utils.urlsafe_base64_encode(domain)
44
+ Auth.request Config.settings[:rs_host] + "/publish/#{encoded_domain}/from/#{bucket}"
45
+ end # publish
46
+
47
+ def unpublish(domain)
48
+ encoded_domain = Utils.urlsafe_base64_encode(domain)
49
+ Auth.request Config.settings[:rs_host] + "/unpublish/#{encoded_domain}"
50
+ end # unpublish
51
+
52
+ def drop(bucket)
53
+ Auth.request Config.settings[:rs_host] + "/drop/#{bucket}"
54
+ end # drop
55
+
56
+ def batch(command, bucket, keys)
57
+ execs = []
58
+ keys.each do |key|
59
+ encoded_uri = encode_entry_uri(bucket, key)
60
+ execs << "op=/#{command}/#{encoded_uri}"
61
+ end
62
+ Auth.request Config.settings[:rs_host] + "/batch", execs.join("&"), {:mime => "application/x-www-form-urlencoded" }
63
+ end # batch
64
+
65
+ def batch_get(bucket, keys)
66
+ batch("get", bucket, keys)
67
+ end # batch_get
68
+
69
+ def batch_stat(bucket, keys)
70
+ batch("stat", bucket, keys)
71
+ end # batch_stat
72
+
73
+ def batch_copy(*args)
74
+ _batch_cp_or_mv('copy', args)
75
+ end # batch_copy
76
+
77
+ def batch_move(*args)
78
+ _batch_cp_or_mv('move', args)
79
+ end # batch_move
80
+
81
+ def batch_delete(bucket, keys)
82
+ batch("delete", bucket, keys)
83
+ end # batch_delete
84
+
85
+ def save_as(bucket, key, source_url, op_params_string)
86
+ encoded_uri = encode_entry_uri(bucket, key)
87
+ save_as_string = '/save-as/' + encoded_uri
88
+ new_url = source_url + '?' + op_params_string + save_as_string
89
+ Auth.request new_url
90
+ end # save_as
91
+
92
+ def image_mogrify_save_as(bucket, key, source_image_url, options)
93
+ mogrify_params_string = Fop::Image.generate_mogrify_params_string(options)
94
+ save_as(bucket, key, source_image_url, mogrify_params_string)
95
+ end # image_mogrify_save_as
96
+
97
+ private
98
+
99
+ def _generate_cp_or_mv_opstr(command, source_bucket, source_key, target_bucket, target_key)
100
+ source_encoded_entry_uri = encode_entry_uri(source_bucket, source_key)
101
+ target_encoded_entry_uri = encode_entry_uri(target_bucket, target_key)
102
+ %Q(/#{command}/#{source_encoded_entry_uri}/#{target_encoded_entry_uri})
103
+ end # _generate_cp_or_mv_opstr
104
+
105
+ def _batch_cp_or_mv(command, *op_args)
106
+ execs = []
107
+ op_args.each do |e|
108
+ execs << 'op=' + _generate_cp_or_mv_opstr(command, e[0], e[1], e[2], e[3]) if e.size == 4
109
+ end
110
+ Auth.request Config.settings[:rs_host] + "/batch", execs.join("&"), {:mime => "application/x-www-form-urlencoded" }
111
+ end # _batch_cp_or_mv
112
+ end
113
+ end # module Storage
114
+ end # module Qiniu
@@ -0,0 +1,35 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module Qiniu
4
+ module Misc
5
+ class << self
6
+ include Utils
7
+
8
+ def set_protected(bucket, protected_mode)
9
+ host = Config.settings[:pub_host]
10
+ Auth.request %Q(#{host}/accessMode/#{bucket}/mode/#{protected_mode})
11
+ end
12
+
13
+ def set_separator(bucket, separator)
14
+ host = Config.settings[:pub_host]
15
+ encoded_separator = Utils.urlsafe_base64_encode(separator)
16
+ Auth.request %Q(#{host}/separator/#{bucket}/sep/#{encoded_separator})
17
+ end
18
+
19
+ def set_style(bucket, name, style)
20
+ host = Config.settings[:pub_host]
21
+ encoded_name = Utils.urlsafe_base64_encode(name)
22
+ encoded_style = Utils.urlsafe_base64_encode(style)
23
+ Auth.request %Q(#{host}/style/#{bucket}/name/#{encoded_name}/style/#{encoded_style})
24
+ end
25
+
26
+ def unset_style(bucket, name)
27
+ host = Config.settings[:pub_host]
28
+ encoded_name = Utils.urlsafe_base64_encode(name)
29
+ Auth.request %Q(#{host}/unstyle/#{bucket}/name/#{encoded_name})
30
+ end
31
+
32
+ end
33
+ end # module Misc
34
+ end # module Qiniu
35
+
@@ -0,0 +1,306 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'zlib'
4
+ require 'yaml'
5
+ require 'tmpdir'
6
+ require 'fileutils'
7
+ require 'mime/types'
8
+ require 'digest/sha1'
9
+ require 'qiniu/abstract'
10
+ require 'qiniu/exceptions'
11
+
12
+ module Qiniu
13
+ module Storage
14
+
15
+ module AbstractClass
16
+ class ChunkProgressNotifier
17
+ include Qiniu::Abstract
18
+ abstract_methods :notify
19
+ # def notify(block_index, block_put_progress); end
20
+ end
21
+
22
+ class BlockProgressNotifier
23
+ include Qiniu::Abstract
24
+ abstract_methods :notify
25
+ # def notify(block_index, checksum); end
26
+ end
27
+ end # module AbstractClass
28
+
29
+ class ChunkProgressNotifier < AbstractClass::ChunkProgressNotifier
30
+ def notify(index, progress)
31
+ logmsg = "chunk #{progress[:offset]/Config.settings[:chunk_size]} in block #{index} successfully uploaded.\n" + progress.to_s
32
+ Utils.debug(logmsg)
33
+ end
34
+ end # class ChunkProgressNotifier
35
+
36
+ class BlockProgressNotifier < AbstractClass::BlockProgressNotifier
37
+ def notify(index, checksum)
38
+ Utils.debug "block #{index}: {ctx: #{checksum}} successfully uploaded."
39
+ Utils.debug "block #{index}: {checksum: #{checksum}} successfully uploaded."
40
+ end
41
+ end # class BlockProgressNotifier
42
+
43
+ class << self
44
+ include Utils
45
+
46
+ def resumable_upload_with_token(uptoken,
47
+ local_file,
48
+ bucket,
49
+ key = nil,
50
+ mime_type = nil,
51
+ custom_meta = nil,
52
+ customer = nil,
53
+ callback_params = nil,
54
+ rotate = nil)
55
+ begin
56
+ ifile = File.open(local_file, 'rb')
57
+ fh = FileData.new(ifile)
58
+ fsize = fh.data_size
59
+ key = Digest::SHA1.hexdigest(local_file + fh.mtime.to_s) if key.nil?
60
+ if mime_type.nil? || mime_type.empty?
61
+ mime = MIME::Types.type_for local_file
62
+ mime_type = mime.empty? ? 'application/octet-stream' : mime[0].content_type
63
+ end
64
+ code, data = _resumable_upload(uptoken, fh, fsize, bucket, key, mime_type, custom_meta, customer, callback_params, rotate)
65
+ [code, data]
66
+ ensure
67
+ ifile.close unless ifile.nil?
68
+ end
69
+ end # resumable_upload_with_token
70
+
71
+ private
72
+
73
+ class FileData
74
+ attr_accessor :fh
75
+ def initialize(fh)
76
+ @fh = fh
77
+ end
78
+ def data_size
79
+ @fh.stat.size
80
+ end
81
+ def get_data(offset, length)
82
+ @fh.seek(offset)
83
+ @fh.read(length)
84
+ end
85
+ def path
86
+ @fh.path
87
+ end
88
+ def mtime
89
+ @fh.mtime
90
+ end
91
+ #delegate :path, :mtime, :to => :fh
92
+ end # class FileData
93
+
94
+ def _new_block_put_progress_data
95
+ {:ctx => nil, :offset => 0, :restsize => nil, :status_code => nil, :host => nil}
96
+ end # _new_block_put_progress_data
97
+
98
+ def _call_binary_with_token(uptoken, url, data, content_type = nil, retry_times = 0)
99
+ options = {
100
+ :method => :post,
101
+ :content_type => 'application/octet-stream',
102
+ :upload_signature_token => uptoken
103
+ }
104
+ options[:content_type] = content_type if !content_type.nil? && !content_type.empty?
105
+ code, data = http_request url, data, options
106
+ unless Utils.is_response_ok?(code)
107
+ retry_times += 1
108
+ if Config.settings[:auto_reconnect] && retry_times < Config.settings[:max_retry_times]
109
+ return _call_binary_with_token(uptoken, url, data, options[:content_type], retry_times)
110
+ end
111
+ end
112
+ [code, data]
113
+ end # _call_binary_with_token
114
+
115
+ def _mkblock(uptoken, block_size, body)
116
+ url = Config.settings[:up_host] + "/mkblk/#{block_size}"
117
+ _call_binary_with_token(uptoken, url, body)
118
+ end # _mkblock
119
+
120
+ def _putblock(uphost, uptoken, ctx, offset, body)
121
+ url = uphost + "/bput/#{ctx}/#{offset}"
122
+ _call_binary_with_token(uptoken, url, body)
123
+ end # _putblock
124
+
125
+ def _resumable_put_block(uptoken,
126
+ fh,
127
+ block_index,
128
+ block_size,
129
+ chunk_size,
130
+ progress,
131
+ retry_times,
132
+ notifier)
133
+ code, data = 0, {}
134
+ fpath = fh.path
135
+
136
+ # this block has never been uploaded.
137
+ if progress[:ctx] == nil || progress[:ctx].empty?
138
+ progress[:offset] = 0
139
+ progress[:restsize] = block_size
140
+ # choose the smaller one
141
+ body_length = [block_size, chunk_size].min
142
+ for i in 1..retry_times
143
+ seek_pos = block_index*Config.settings[:block_size]
144
+ body = fh.get_data(seek_pos, body_length)
145
+ result_length = body.length
146
+ if result_length != body_length
147
+ raise FileSeekReadError.new(fpath, block_index, seek_pos, body_length, result_length)
148
+ end
149
+ code, data = _mkblock(uptoken, block_size, body)
150
+ body_crc32 = Zlib.crc32(body)
151
+ if Utils.is_response_ok?(code) && data["crc32"] == body_crc32
152
+ progress[:ctx] = data["ctx"]
153
+ progress[:offset] = body_length
154
+ progress[:restsize] = block_size - body_length
155
+ progress[:status_code] = code
156
+ progress[:host] = data["host"]
157
+ if !notifier.nil? && notifier.respond_to?("notify")
158
+ notifier.notify(block_index, progress)
159
+ end
160
+ break
161
+ elsif i == retry_times && data["crc32"] != body_crc32
162
+ Log.logger.error %Q(Uploading block error. Expected crc32: #{body_crc32}, but got: #{data["crc32"]})
163
+ end
164
+ end
165
+ elsif progress[:offset] + progress[:restsize] != block_size
166
+ raise BlockSizeNotMathchError.new(fpath, block_index, progress[:offset], progress[:restsize], block_size)
167
+ end
168
+
169
+ # loop uploading other chunks except the first one
170
+ while progress[:restsize].to_i > 0 && progress[:restsize] < block_size
171
+ # choose the smaller one
172
+ body_length = [progress[:restsize], chunk_size].min
173
+ for i in 1..retry_times
174
+ seek_pos = block_index*Config.settings[:block_size] + progress[:offset]
175
+ body = fh.get_data(seek_pos, body_length)
176
+ result_length = body.length
177
+ if result_length != body_length
178
+ raise FileSeekReadError.new(fpath, block_index, seek_pos, body_length, result_length)
179
+ end
180
+ code, data = _putblock(progress[:host], uptoken, progress[:ctx], progress[:offset], body)
181
+ body_crc32 = Zlib.crc32(body)
182
+ if Utils.is_response_ok?(code) && data["crc32"] == body_crc32
183
+ progress[:ctx] = data["ctx"]
184
+ progress[:offset] += body_length
185
+ progress[:restsize] -= body_length
186
+ progress[:status_code] = code
187
+ progress[:host] = data["host"]
188
+ if !notifier.nil? && notifier.respond_to?("notify")
189
+ notifier.notify(block_index, progress)
190
+ end
191
+ break
192
+ elsif i == retry_times && data["crc32"] != body_crc32
193
+ Log.logger.error %Q(Uploading block error. Expected crc32: #{body_crc32}, but got: #{data["crc32"]})
194
+ end
195
+ end
196
+ end
197
+ # return
198
+ return [code, data]
199
+ end # _resumable_put_block
200
+
201
+ def _block_count(fsize)
202
+ ((fsize + Config.settings[:block_size] - 1) / Config.settings[:block_size]).to_i
203
+ end # _block_count
204
+
205
+ def _resumable_put(uptoken,
206
+ fh,
207
+ checksums,
208
+ progresses,
209
+ block_notifier = nil,
210
+ chunk_notifier = nil)
211
+ code, data = 0, {}
212
+ fsize = fh.data_size
213
+ block_count = _block_count(fsize)
214
+ checksum_count = checksums.length
215
+ progress_count = progresses.length
216
+ if checksum_count != block_count || progress_count != block_count
217
+ raise BlockCountNotMathchError.new(fh.path, block_count, checksum_count, progress_count)
218
+ end
219
+ 0.upto(block_count-1).each do |block_index|
220
+ if checksums[block_index].nil? || checksums[block_index].empty?
221
+ block_size = Config.settings[:block_size]
222
+ if block_index == block_count - 1
223
+ block_size = fsize - block_index*Config.settings[:block_size]
224
+ end
225
+ if progresses[block_index].nil?
226
+ progresses[block_index] = _new_block_put_progress_data
227
+ end
228
+ #code, data = _resumable_put_block(uptoken, fh, block_index, block_size, Config.settings[:chunk_size], progresses[block_index], Config.settings[:max_retry_times], chunk_notifier)
229
+ # Put the whole block as a chunk
230
+ code, data = _resumable_put_block(uptoken, fh, block_index, block_size, block_size, progresses[block_index], Config.settings[:max_retry_times], chunk_notifier)
231
+ if Utils.is_response_ok?(code)
232
+ #checksums[block_index] = data["checksum"]
233
+ checksums[block_index] = data["ctx"]
234
+ if !block_notifier.nil? && block_notifier.respond_to?("notify")
235
+ block_notifier.notify(block_index, checksums[block_index])
236
+ end
237
+ end
238
+ end
239
+ end
240
+ return [code, data]
241
+ end # _resumable_put
242
+
243
+ def _mkfile(uphost,
244
+ uptoken,
245
+ entry_uri,
246
+ fsize,
247
+ checksums,
248
+ mime_type = nil,
249
+ custom_meta = nil,
250
+ customer = nil,
251
+ callback_params = nil,
252
+ rotate = nil)
253
+ path = '/rs-mkfile/' + Utils.urlsafe_base64_encode(entry_uri) + "/fsize/#{fsize}"
254
+ path += '/mimeType/' + Utils.urlsafe_base64_encode(mime_type) if !mime_type.nil? && !mime_type.empty?
255
+ path += '/meta/' + Utils.urlsafe_base64_encode(custom_meta) if !custom_meta.nil? && !custom_meta.empty?
256
+ path += '/customer/' + customer if !customer.nil? && !customer.empty?
257
+ callback_query_string = Utils.generate_query_string(callback_params) if !callback_params.nil? && !callback_params.empty?
258
+ path += '/params/' + Utils.urlsafe_base64_encode(callback_query_string) if !callback_query_string.nil? && !callback_query_string.empty?
259
+ path += '/rotate/' + rotate if !rotate.nil? && rotate.to_i >= 0
260
+ url = uphost + path
261
+ #body = ''
262
+ #checksums.each do |checksum|
263
+ # body += Utils.urlsafe_base64_decode(checksum)
264
+ #end
265
+ body = checksums.join(',')
266
+ _call_binary_with_token(uptoken, url, body, 'text/plain')
267
+ end # _mkfile
268
+
269
+ def _resumable_upload(uptoken,
270
+ fh,
271
+ fsize,
272
+ bucket,
273
+ key,
274
+ mime_type = nil,
275
+ custom_meta = nil,
276
+ customer = nil,
277
+ callback_params = nil,
278
+ rotate = nil)
279
+
280
+ block_count = _block_count(fsize)
281
+
282
+ chunk_notifier = ChunkProgressNotifier.new()
283
+ block_notifier = BlockProgressNotifier.new()
284
+
285
+ progresses = []
286
+ block_count.times{progresses << _new_block_put_progress_data}
287
+ checksums = []
288
+ block_count.times{checksums << ''}
289
+
290
+ code, data = _resumable_put(uptoken, fh, checksums, progresses, block_notifier, chunk_notifier)
291
+
292
+ if Utils.is_response_ok?(code)
293
+ uphost = data["host"]
294
+ entry_uri = bucket + ':' + key
295
+ code, data = _mkfile(uphost, uptoken, entry_uri, fsize, checksums, mime_type, custom_meta, customer, callback_params, rotate)
296
+ end
297
+
298
+ if Utils.is_response_ok?(code)
299
+ Utils.debug "File #{fh.path} {size: #{fsize}} successfully uploaded."
300
+ end
301
+
302
+ [code, data]
303
+ end # _resumable_upload
304
+ end # self class
305
+ end # module Storage
306
+ end # module Qiniu