qiniu_jxb 6.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/.gitignore +18 -0
  2. data/.rspec +1 -0
  3. data/.travis.yml +9 -0
  4. data/CHANGELOG.md +118 -0
  5. data/Gemfile +8 -0
  6. data/Gemfile.lock +37 -0
  7. data/LICENSE +22 -0
  8. data/README.md +47 -0
  9. data/Rakefile +21 -0
  10. data/docs/README.md +790 -0
  11. data/lib/qiniu-rs.rb +2 -0
  12. data/lib/qiniu.rb +209 -0
  13. data/lib/qiniu/abstract.rb +22 -0
  14. data/lib/qiniu/adt.rb +46 -0
  15. data/lib/qiniu/auth.rb +234 -0
  16. data/lib/qiniu/config.rb +58 -0
  17. data/lib/qiniu/exceptions.rb +120 -0
  18. data/lib/qiniu/fop.rb +4 -0
  19. data/lib/qiniu/http.rb +137 -0
  20. data/lib/qiniu/image.rb +38 -0
  21. data/lib/qiniu/log.rb +15 -0
  22. data/lib/qiniu/management.rb +128 -0
  23. data/lib/qiniu/misc.rb +33 -0
  24. data/lib/qiniu/pfop.rb +124 -0
  25. data/lib/qiniu/resumable_upload.rb +319 -0
  26. data/lib/qiniu/storage.rb +5 -0
  27. data/lib/qiniu/tokens/access_token.rb +21 -0
  28. data/lib/qiniu/tokens/download_token.rb +31 -0
  29. data/lib/qiniu/tokens/qbox_token.rb +38 -0
  30. data/lib/qiniu/tokens/upload_token.rb +47 -0
  31. data/lib/qiniu/upload.rb +138 -0
  32. data/lib/qiniu/utils.rb +109 -0
  33. data/lib/qiniu/version.rb +17 -0
  34. data/qiniu.gemspec +29 -0
  35. data/spec/qiniu/abstract_spec.rb +30 -0
  36. data/spec/qiniu/auth_spec.rb +81 -0
  37. data/spec/qiniu/image_logo_for_test.png +0 -0
  38. data/spec/qiniu/image_spec.rb +89 -0
  39. data/spec/qiniu/management_spec.rb +156 -0
  40. data/spec/qiniu/misc_spec.rb +59 -0
  41. data/spec/qiniu/pfop_spec.rb +89 -0
  42. data/spec/qiniu/qiniu_spec.rb +329 -0
  43. data/spec/qiniu/tokens/qbox_token_spec.rb +29 -0
  44. data/spec/qiniu/upload_spec.rb +308 -0
  45. data/spec/qiniu/utils_spec.rb +49 -0
  46. data/spec/qiniu/version_spec.rb +10 -0
  47. data/spec/spec_helper.rb +19 -0
  48. metadata +220 -0
@@ -0,0 +1,58 @@
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
+ :up_host => "http://up.qiniu.com",
24
+ :pub_host => "http://pu.qbox.me:10200",
25
+ :eu_host => "http://eu.qbox.me",
26
+ :access_key => "",
27
+ :secret_key => "",
28
+ :auto_reconnect => true,
29
+ :max_retry_times => 3,
30
+ :block_size => 1024*1024*4,
31
+ :chunk_size => 1024*256,
32
+ :enable_debug => true,
33
+ :tmpdir => Dir.tmpdir + File::SEPARATOR + 'QiniuRuby'
34
+ }
35
+
36
+ REQUIRED_OPTION_KEYS = [:access_key, :secret_key]
37
+
38
+ attr_reader :settings, :default_params
39
+
40
+ def load config_file
41
+ if File.exist?(config_file)
42
+ config_options = YAML.load_file(config_file)
43
+ initialize_connect(config_options)
44
+ else
45
+ raise MissingConfError, config_file
46
+ end
47
+ end
48
+
49
+ def initialize_connect options = {}
50
+ @settings = DEFAULT_OPTIONS.merge(options)
51
+ REQUIRED_OPTION_KEYS.each do |opt|
52
+ raise MissingArgsError, [opt] unless @settings.has_key?(opt)
53
+ end
54
+ end
55
+
56
+ end
57
+ end # module Config
58
+ 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
@@ -0,0 +1,4 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'qiniu/image'
4
+ require 'qiniu/pfop'
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
@@ -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
data/lib/qiniu/log.rb ADDED
@@ -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,128 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # vim: sw=2 ts=2
3
+
4
+ require 'qiniu/http'
5
+
6
+ module Qiniu
7
+ module Storage
8
+ class << self
9
+ include Utils
10
+
11
+ public
12
+ def buckets
13
+ url = Config.settings[:rs_host] + '/buckets'
14
+ return HTTP.management_post(url)
15
+ end # buckets
16
+
17
+ PRIVATE_BUCKET = 0
18
+ PUBLIC_BUCKET = 1
19
+
20
+ def mkbucket(bucket_name, is_public = PUBLIC_BUCKET)
21
+ url = Config.settings[:rs_host] + '/mkbucket2/' + bucket_name + '/public/' + is_public.to_s
22
+ return HTTP.management_post(url)
23
+ end # mkbucket
24
+
25
+ def make_a_private_bucket(bucket_name)
26
+ return mkbucket(bucket_name, PRIVATE_BUCKET)
27
+ end # make_a_private_bucket
28
+
29
+ def make_a_public_bucket(bucket_name)
30
+ return mkbucket(bucket_name, PUBLIC_BUCKET)
31
+ end # make_a_public_bucket
32
+
33
+ def stat(bucket, key)
34
+ url = Config.settings[:rs_host] + '/stat/' + encode_entry_uri(bucket, key)
35
+ return HTTP.management_post(url)
36
+ end # stat
37
+
38
+ def get(bucket, key, save_as = nil, expires_in = nil, version = nil)
39
+ url = Config.settings[:rs_host] + '/get/' + encode_entry_uri(bucket, key)
40
+ url += '/base/' + version unless version.nil?
41
+ url += '/attName/' + Utils.urlsafe_base64_encode(save_as) unless save_as.nil?
42
+ url += '/expires/' + expires_in.to_s if !expires_in.nil? && expires_in > 0
43
+ return HTTP.management_post(url)
44
+ end # get
45
+
46
+ def copy(source_bucket, source_key, target_bucket, target_key)
47
+ uri = _generate_cp_or_mv_opstr('copy', source_bucket, source_key, target_bucket, target_key)
48
+ url = Config.settings[:rs_host] + uri
49
+ return HTTP.management_post(url)
50
+ end # copy
51
+
52
+ def move(source_bucket, source_key, target_bucket, target_key)
53
+ uri = _generate_cp_or_mv_opstr('move', source_bucket, source_key, target_bucket, target_key)
54
+ url = Config.settings[:rs_host] + uri
55
+ return HTTP.management_post(url)
56
+ end # move
57
+
58
+ def delete(bucket, key)
59
+ url = Config.settings[:rs_host] + '/delete/' + encode_entry_uri(bucket, key)
60
+ return HTTP.management_post(url)
61
+ end # delete
62
+
63
+ def drop(bucket)
64
+ url = Config.settings[:rs_host] + "/drop/#{bucket}"
65
+ return HTTP.management_post(url)
66
+ end # drop
67
+
68
+ def batch(command, bucket, keys)
69
+ execs = []
70
+ keys.each do |key|
71
+ encoded_uri = encode_entry_uri(bucket, key)
72
+ execs << "op=/#{command}/#{encoded_uri}"
73
+ end
74
+ url = Config.settings[:rs_host] + "/batch"
75
+ return HTTP.management_post(url, execs.join("&"))
76
+ end # batch
77
+
78
+ def batch_get(bucket, keys)
79
+ batch("get", bucket, keys)
80
+ end # batch_get
81
+
82
+ def batch_stat(bucket, keys)
83
+ batch("stat", bucket, keys)
84
+ end # batch_stat
85
+
86
+ def batch_copy(*args)
87
+ _batch_cp_or_mv('copy', args)
88
+ end # batch_copy
89
+
90
+ def batch_move(*args)
91
+ _batch_cp_or_mv('move', args)
92
+ end # batch_move
93
+
94
+ def batch_delete(bucket, keys)
95
+ batch("delete", bucket, keys)
96
+ end # batch_delete
97
+
98
+ def save_as(bucket, key, source_url, op_params_string)
99
+ encoded_uri = encode_entry_uri(bucket, key)
100
+ save_as_string = '/save-as/' + encoded_uri
101
+ new_url = source_url + '?' + op_params_string + save_as_string
102
+ return HTTP.management_post(new_url)
103
+ end # save_as
104
+
105
+ def image_mogrify_save_as(bucket, key, source_image_url, options)
106
+ mogrify_params_string = Fop::Image.generate_mogrify_params_string(options)
107
+ save_as(bucket, key, source_image_url, mogrify_params_string)
108
+ end # image_mogrify_save_as
109
+
110
+ private
111
+
112
+ def _generate_cp_or_mv_opstr(command, source_bucket, source_key, target_bucket, target_key)
113
+ source_encoded_entry_uri = encode_entry_uri(source_bucket, source_key)
114
+ target_encoded_entry_uri = encode_entry_uri(target_bucket, target_key)
115
+ %Q(/#{command}/#{source_encoded_entry_uri}/#{target_encoded_entry_uri})
116
+ end # _generate_cp_or_mv_opstr
117
+
118
+ def _batch_cp_or_mv(command, *op_args)
119
+ execs = []
120
+ op_args.each do |e|
121
+ execs << 'op=' + _generate_cp_or_mv_opstr(command, e[0], e[1], e[2], e[3]) if e.size == 4
122
+ end
123
+ url = Config.settings[:rs_host] + "/batch"
124
+ return HTTP.management_post(url, execs.join("&"))
125
+ end # _batch_cp_or_mv
126
+ end
127
+ end # module Storage
128
+ end # module Qiniu