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.
@@ -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