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,166 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
# vim: sw=2 ts=2
|
3
|
+
|
4
|
+
require 'qiniu/adt'
|
5
|
+
require 'qiniu/http'
|
6
|
+
|
7
|
+
module Qiniu
|
8
|
+
module Storage
|
9
|
+
class ListPolicy
|
10
|
+
include ADT::Policy
|
11
|
+
|
12
|
+
private
|
13
|
+
def initialize(bucket,
|
14
|
+
limit = 1000,
|
15
|
+
prefix = '',
|
16
|
+
delimiter = '')
|
17
|
+
@bucket = bucket
|
18
|
+
@limit = limit
|
19
|
+
@prefix = prefix
|
20
|
+
@delimiter = delimiter
|
21
|
+
end # initialize
|
22
|
+
|
23
|
+
public
|
24
|
+
PARAMS = {
|
25
|
+
# 字符串类型参数
|
26
|
+
:bucket => "bucket",
|
27
|
+
:prefix => "prefix",
|
28
|
+
:delimiter => "delimiter",
|
29
|
+
:marker => "marker",
|
30
|
+
|
31
|
+
# 数值类型参数
|
32
|
+
:limit => "limit"
|
33
|
+
} # PARAMS
|
34
|
+
|
35
|
+
PARAMS.each_pair do |key, fld|
|
36
|
+
attr_accessor key
|
37
|
+
end
|
38
|
+
|
39
|
+
def params
|
40
|
+
return PARAMS
|
41
|
+
end # params
|
42
|
+
|
43
|
+
alias :to_s :to_query_string
|
44
|
+
end # class ListPolicy
|
45
|
+
|
46
|
+
class << self
|
47
|
+
include Utils
|
48
|
+
|
49
|
+
public
|
50
|
+
def buckets
|
51
|
+
url = Config.settings[:rs_host] + '/buckets'
|
52
|
+
return HTTP.management_post(url)
|
53
|
+
end # buckets
|
54
|
+
|
55
|
+
def stat(bucket, key)
|
56
|
+
url = Config.settings[:rs_host] + '/stat/' + encode_entry_uri(bucket, key)
|
57
|
+
return HTTP.management_post(url)
|
58
|
+
end # stat
|
59
|
+
|
60
|
+
def get(bucket, key, save_as = nil, expires_in = nil, version = nil)
|
61
|
+
url = Config.settings[:rs_host] + '/get/' + encode_entry_uri(bucket, key)
|
62
|
+
url += '/base/' + version unless version.nil?
|
63
|
+
url += '/attName/' + Utils.urlsafe_base64_encode(save_as) unless save_as.nil?
|
64
|
+
url += '/expires/' + expires_in.to_s if !expires_in.nil? && expires_in > 0
|
65
|
+
return HTTP.management_post(url)
|
66
|
+
end # get
|
67
|
+
|
68
|
+
def copy(source_bucket, source_key, target_bucket, target_key)
|
69
|
+
uri = _generate_cp_or_mv_opstr('copy', source_bucket, source_key, target_bucket, target_key)
|
70
|
+
url = Config.settings[:rs_host] + uri
|
71
|
+
return HTTP.management_post(url)
|
72
|
+
end # copy
|
73
|
+
|
74
|
+
def move(source_bucket, source_key, target_bucket, target_key)
|
75
|
+
uri = _generate_cp_or_mv_opstr('move', source_bucket, source_key, target_bucket, target_key)
|
76
|
+
url = Config.settings[:rs_host] + uri
|
77
|
+
return HTTP.management_post(url)
|
78
|
+
end # move
|
79
|
+
|
80
|
+
def delete(bucket, key)
|
81
|
+
url = Config.settings[:rs_host] + '/delete/' + encode_entry_uri(bucket, key)
|
82
|
+
return HTTP.management_post(url)
|
83
|
+
end # delete
|
84
|
+
|
85
|
+
def batch(command, bucket, keys)
|
86
|
+
execs = []
|
87
|
+
keys.each do |key|
|
88
|
+
encoded_uri = encode_entry_uri(bucket, key)
|
89
|
+
execs << "op=/#{command}/#{encoded_uri}"
|
90
|
+
end
|
91
|
+
url = Config.settings[:rs_host] + "/batch"
|
92
|
+
return HTTP.management_post(url, execs.join("&"))
|
93
|
+
end # batch
|
94
|
+
|
95
|
+
def batch_get(bucket, keys)
|
96
|
+
batch("get", bucket, keys)
|
97
|
+
end # batch_get
|
98
|
+
|
99
|
+
def batch_stat(bucket, keys)
|
100
|
+
batch("stat", bucket, keys)
|
101
|
+
end # batch_stat
|
102
|
+
|
103
|
+
def batch_copy(*args)
|
104
|
+
_batch_cp_or_mv('copy', args)
|
105
|
+
end # batch_copy
|
106
|
+
|
107
|
+
def batch_move(*args)
|
108
|
+
_batch_cp_or_mv('move', args)
|
109
|
+
end # batch_move
|
110
|
+
|
111
|
+
def batch_delete(bucket, keys)
|
112
|
+
batch("delete", bucket, keys)
|
113
|
+
end # batch_delete
|
114
|
+
|
115
|
+
def save_as(bucket, key, source_url, op_params_string)
|
116
|
+
encoded_uri = encode_entry_uri(bucket, key)
|
117
|
+
save_as_string = '/save-as/' + encoded_uri
|
118
|
+
new_url = source_url + '?' + op_params_string + save_as_string
|
119
|
+
return HTTP.management_post(new_url)
|
120
|
+
end # save_as
|
121
|
+
|
122
|
+
def image_mogrify_save_as(bucket, key, source_image_url, options)
|
123
|
+
mogrify_params_string = Fop::Image.generate_mogrify_params_string(options)
|
124
|
+
save_as(bucket, key, source_image_url, mogrify_params_string)
|
125
|
+
end # image_mogrify_save_as
|
126
|
+
|
127
|
+
def list(list_policy)
|
128
|
+
url = Config.settings[:rsf_host] + '/list?' + list_policy.to_query_string()
|
129
|
+
|
130
|
+
resp_code, resp_body, resp_headers = HTTP.management_post(url)
|
131
|
+
if resp_code == 0 || resp_code > 299 then
|
132
|
+
has_more = false
|
133
|
+
return resp_code, resp_body, resp_headers, has_more, list_policy
|
134
|
+
end
|
135
|
+
|
136
|
+
has_more = (resp_body['marker'].is_a?(String) && resp_body['marker'] != '')
|
137
|
+
if has_more then
|
138
|
+
new_list_policy = list_policy.clone()
|
139
|
+
new_list_policy.marker = resp_body['marker']
|
140
|
+
else
|
141
|
+
new_list_policy = list_policy
|
142
|
+
end
|
143
|
+
|
144
|
+
return resp_code, resp_body, resp_headers, has_more, new_list_policy
|
145
|
+
end # list
|
146
|
+
|
147
|
+
private
|
148
|
+
|
149
|
+
def _generate_cp_or_mv_opstr(command, source_bucket, source_key, target_bucket, target_key)
|
150
|
+
source_encoded_entry_uri = encode_entry_uri(source_bucket, source_key)
|
151
|
+
target_encoded_entry_uri = encode_entry_uri(target_bucket, target_key)
|
152
|
+
%Q(/#{command}/#{source_encoded_entry_uri}/#{target_encoded_entry_uri})
|
153
|
+
end # _generate_cp_or_mv_opstr
|
154
|
+
|
155
|
+
def _batch_cp_or_mv(command, *op_args)
|
156
|
+
execs = []
|
157
|
+
op_args.each do |e|
|
158
|
+
execs << 'op=' + _generate_cp_or_mv_opstr(command, e[0], e[1], e[2], e[3]) if e.size == 4
|
159
|
+
end
|
160
|
+
url = Config.settings[:rs_host] + "/batch"
|
161
|
+
return HTTP.management_post(url, execs.join("&"))
|
162
|
+
end # _batch_cp_or_mv
|
163
|
+
|
164
|
+
end # class << self
|
165
|
+
end # module Storage
|
166
|
+
end # module Qiniu
|
data/lib/qiniu/misc.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'qiniu/http'
|
4
|
+
|
5
|
+
module Qiniu
|
6
|
+
module Misc
|
7
|
+
class << self
|
8
|
+
def set_protected(bucket, protected_mode)
|
9
|
+
url = Config.settings[:pub_host] + %Q(/accessMode/#{bucket}/mode/#{protected_mode})
|
10
|
+
return HTTP.management_post(url)
|
11
|
+
end # set_protected
|
12
|
+
|
13
|
+
def set_separator(bucket, separator)
|
14
|
+
encoded_separator = Utils.urlsafe_base64_encode(separator)
|
15
|
+
url = Config.settings[:pub_host] + %Q(/separator/#{bucket}/sep/#{encoded_separator})
|
16
|
+
return HTTP.management_post(url)
|
17
|
+
end # set_separator
|
18
|
+
|
19
|
+
def set_style(bucket, name, style)
|
20
|
+
encoded_name = Utils.urlsafe_base64_encode(name)
|
21
|
+
encoded_style = Utils.urlsafe_base64_encode(style)
|
22
|
+
url = Config.settings[:pub_host] + %Q(/style/#{bucket}/name/#{encoded_name}/style/#{encoded_style})
|
23
|
+
return HTTP.management_post(url)
|
24
|
+
end # set_style
|
25
|
+
|
26
|
+
def unset_style(bucket, name)
|
27
|
+
encoded_name = Utils.urlsafe_base64_encode(name)
|
28
|
+
url = Config.settings[:pub_host] + %Q(/unstyle/#{bucket}/name/#{encoded_name})
|
29
|
+
return HTTP.management_post(url)
|
30
|
+
end # unset_style
|
31
|
+
end # class << self
|
32
|
+
|
33
|
+
end # module Misc
|
34
|
+
end # module Qiniu
|
35
|
+
|
data/lib/qiniu/pfop.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
# vim: sw=2 ts=2
|
3
|
+
|
4
|
+
require 'qiniu/adt'
|
5
|
+
require 'qiniu/http'
|
6
|
+
|
7
|
+
module Qiniu
|
8
|
+
module Fop
|
9
|
+
module Persistance
|
10
|
+
|
11
|
+
class PfopPolicy
|
12
|
+
include ADT::Policy
|
13
|
+
|
14
|
+
private
|
15
|
+
def initialize(bucket,
|
16
|
+
key,
|
17
|
+
fops,
|
18
|
+
notify_url)
|
19
|
+
@bucket = bucket
|
20
|
+
@key = key
|
21
|
+
@notify_url = notify_url
|
22
|
+
|
23
|
+
self.fops!(fops)
|
24
|
+
end # initialize
|
25
|
+
|
26
|
+
public
|
27
|
+
PARAMS = {
|
28
|
+
# 字符串类型参数
|
29
|
+
:bucket => "bucket",
|
30
|
+
:key => "key",
|
31
|
+
:fops => "fops",
|
32
|
+
:notify_url => "notifyURL",
|
33
|
+
:pipeline => "pipeline",
|
34
|
+
|
35
|
+
# 数值类型参数
|
36
|
+
:force => "force"
|
37
|
+
} # PARAMS
|
38
|
+
|
39
|
+
PARAMS.each_pair do |key, fld|
|
40
|
+
attr_accessor key
|
41
|
+
end
|
42
|
+
|
43
|
+
def params
|
44
|
+
return PARAMS
|
45
|
+
end # params
|
46
|
+
|
47
|
+
def fops! (fops)
|
48
|
+
if fops.is_a?(Hash) then
|
49
|
+
fops = fops.values
|
50
|
+
end
|
51
|
+
|
52
|
+
if fops.is_a?(Array) then
|
53
|
+
new_fops = []
|
54
|
+
fops.each do |v|
|
55
|
+
if v.is_a?(ApiSpecification) then
|
56
|
+
new_fops.push(v.to_s)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
@fops = new_fops.join(";")
|
61
|
+
else
|
62
|
+
@fops = fops.to_s
|
63
|
+
end
|
64
|
+
end # fops!
|
65
|
+
|
66
|
+
def force!
|
67
|
+
@force = 1
|
68
|
+
end # force!
|
69
|
+
|
70
|
+
alias :to_s :to_json
|
71
|
+
end # class PfopPolicy
|
72
|
+
|
73
|
+
class << self
|
74
|
+
|
75
|
+
API_HOST = 'http://api.qiniu.com'
|
76
|
+
|
77
|
+
PFOP_URL = API_HOST + '/pfop/'
|
78
|
+
|
79
|
+
def pfop (args)
|
80
|
+
### 生成fop指令串
|
81
|
+
if args.is_a?(PfopPolicy) then
|
82
|
+
# PfopPolicy的各个字段按固定顺序组织
|
83
|
+
body = args.to_query_string()
|
84
|
+
elsif args.is_a?(Hash) then
|
85
|
+
# 无法保证固定字段顺序
|
86
|
+
body = HTTP.generate_query_string(args)
|
87
|
+
else
|
88
|
+
# 由调用者保证固定字段顺序
|
89
|
+
body = args.to_s
|
90
|
+
end
|
91
|
+
|
92
|
+
### 发送请求
|
93
|
+
return HTTP.management_post(PFOP_URL, body)
|
94
|
+
end # pfop
|
95
|
+
|
96
|
+
PREFOP_URL = API_HOST + '/status/get/prefop?id='
|
97
|
+
|
98
|
+
def prefop (persistent_id)
|
99
|
+
### 抽取persistentId
|
100
|
+
if persistent_id.is_a?(Hash) then
|
101
|
+
pid = persistent_id['persistentId']
|
102
|
+
else
|
103
|
+
pid = persistent_id.to_s
|
104
|
+
end
|
105
|
+
|
106
|
+
### 发送请求
|
107
|
+
url = PREFOP_URL + pid
|
108
|
+
return HTTP.api_get(url)
|
109
|
+
end # prefop
|
110
|
+
|
111
|
+
def generate_p1_url (url, fop)
|
112
|
+
# 如果fop是ApiSpecification,则各字段按固定顺序组织,保证一致性
|
113
|
+
# 否则由调用者保证固定字段顺序
|
114
|
+
fop = CGI.escape(fop.to_s).gsub('+', '%20')
|
115
|
+
|
116
|
+
### 生成url
|
117
|
+
return url + '?p/1/' + fop
|
118
|
+
end # generate_pl_url
|
119
|
+
|
120
|
+
end # class << self
|
121
|
+
|
122
|
+
end # module Persistance
|
123
|
+
end # module Fop
|
124
|
+
end # module Qiniu
|
@@ -0,0 +1,319 @@
|
|
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
|
+
:headers => {
|
101
|
+
:content_type => 'application/octet-stream',
|
102
|
+
'Authorization' => 'UpToken ' + uptoken
|
103
|
+
}
|
104
|
+
}
|
105
|
+
if !content_type.nil? && !content_type.empty? then
|
106
|
+
options[:headers][:content_type] = content_type
|
107
|
+
end
|
108
|
+
|
109
|
+
code, data, raw_headers = HTTP.api_post(url, data, options)
|
110
|
+
unless HTTP.is_response_ok?(code)
|
111
|
+
retry_times += 1
|
112
|
+
if Config.settings[:auto_reconnect] && retry_times < Config.settings[:max_retry_times]
|
113
|
+
return _call_binary_with_token(uptoken, url, data, options[:content_type], retry_times)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
return code, data, raw_headers
|
117
|
+
end # _call_binary_with_token
|
118
|
+
|
119
|
+
def _mkblock(uptoken, block_size, body)
|
120
|
+
url = Config.settings[:up_host] + "/mkblk/#{block_size}"
|
121
|
+
_call_binary_with_token(uptoken, url, body)
|
122
|
+
end # _mkblock
|
123
|
+
|
124
|
+
def _putblock(uphost, uptoken, ctx, offset, body)
|
125
|
+
url = uphost + "/bput/#{ctx}/#{offset}"
|
126
|
+
_call_binary_with_token(uptoken, url, body)
|
127
|
+
end # _putblock
|
128
|
+
|
129
|
+
def _resumable_put_block(uptoken,
|
130
|
+
fh,
|
131
|
+
block_index,
|
132
|
+
block_size,
|
133
|
+
chunk_size,
|
134
|
+
progress,
|
135
|
+
retry_times,
|
136
|
+
notifier)
|
137
|
+
code, data = 0, {}
|
138
|
+
fpath = fh.path
|
139
|
+
|
140
|
+
# this block has never been uploaded.
|
141
|
+
if progress[:ctx] == nil || progress[:ctx].empty?
|
142
|
+
progress[:offset] = 0
|
143
|
+
progress[:restsize] = block_size
|
144
|
+
# choose the smaller one
|
145
|
+
body_length = [block_size, chunk_size].min
|
146
|
+
for i in 1..retry_times
|
147
|
+
seek_pos = block_index*Config.settings[:block_size]
|
148
|
+
body = fh.get_data(seek_pos, body_length)
|
149
|
+
result_length = body.length
|
150
|
+
if result_length != body_length
|
151
|
+
raise FileSeekReadError.new(fpath, block_index, seek_pos, body_length, result_length)
|
152
|
+
end
|
153
|
+
|
154
|
+
code, data, raw_headers = _mkblock(uptoken, block_size, body)
|
155
|
+
Utils.debug "Mkblk : #{code.inspect} #{data.inspect} #{raw_headers.inspect}"
|
156
|
+
|
157
|
+
body_crc32 = Zlib.crc32(body)
|
158
|
+
if HTTP.is_response_ok?(code) && data["crc32"] == body_crc32
|
159
|
+
progress[:ctx] = data["ctx"]
|
160
|
+
progress[:offset] = body_length
|
161
|
+
progress[:restsize] = block_size - body_length
|
162
|
+
progress[:status_code] = code
|
163
|
+
progress[:host] = data["host"]
|
164
|
+
if !notifier.nil? && notifier.respond_to?("notify")
|
165
|
+
notifier.notify(block_index, progress)
|
166
|
+
end
|
167
|
+
break
|
168
|
+
elsif i == retry_times && data["crc32"] != body_crc32
|
169
|
+
Log.logger.error %Q(Uploading block error. Expected crc32: #{body_crc32}, but got: #{data["crc32"]})
|
170
|
+
return code, data, raw_headers
|
171
|
+
end
|
172
|
+
end
|
173
|
+
elsif progress[:offset] + progress[:restsize] != block_size
|
174
|
+
raise BlockSizeNotMathchError.new(fpath, block_index, progress[:offset], progress[:restsize], block_size)
|
175
|
+
end
|
176
|
+
|
177
|
+
# loop uploading other chunks except the first one
|
178
|
+
while progress[:restsize].to_i > 0 && progress[:restsize] < block_size
|
179
|
+
# choose the smaller one
|
180
|
+
body_length = [progress[:restsize], chunk_size].min
|
181
|
+
for i in 1..retry_times
|
182
|
+
seek_pos = block_index*Config.settings[:block_size] + progress[:offset]
|
183
|
+
body = fh.get_data(seek_pos, body_length)
|
184
|
+
result_length = body.length
|
185
|
+
if result_length != body_length
|
186
|
+
raise FileSeekReadError.new(fpath, block_index, seek_pos, body_length, result_length)
|
187
|
+
end
|
188
|
+
|
189
|
+
code, data, raw_headers = _putblock(progress[:host], uptoken, progress[:ctx], progress[:offset], body)
|
190
|
+
Utils.debug "Bput : #{code.inspect} #{data.inspect} #{raw_headers.inspect}"
|
191
|
+
|
192
|
+
body_crc32 = Zlib.crc32(body)
|
193
|
+
if HTTP.is_response_ok?(code) && data["crc32"] == body_crc32
|
194
|
+
progress[:ctx] = data["ctx"]
|
195
|
+
progress[:offset] += body_length
|
196
|
+
progress[:restsize] -= body_length
|
197
|
+
progress[:status_code] = code
|
198
|
+
progress[:host] = data["host"]
|
199
|
+
if !notifier.nil? && notifier.respond_to?("notify")
|
200
|
+
notifier.notify(block_index, progress)
|
201
|
+
end
|
202
|
+
break
|
203
|
+
elsif i == retry_times && data["crc32"] != body_crc32
|
204
|
+
Log.logger.error %Q(Uploading block error. Expected crc32: #{body_crc32}, but got: #{data["crc32"]})
|
205
|
+
return code, data, raw_headers
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
# return
|
210
|
+
return code, data, raw_headers
|
211
|
+
end # _resumable_put_block
|
212
|
+
|
213
|
+
def _block_count(fsize)
|
214
|
+
((fsize + Config.settings[:block_size] - 1) / Config.settings[:block_size]).to_i
|
215
|
+
end # _block_count
|
216
|
+
|
217
|
+
def _resumable_put(uptoken,
|
218
|
+
fh,
|
219
|
+
checksums,
|
220
|
+
progresses,
|
221
|
+
block_notifier = nil,
|
222
|
+
chunk_notifier = nil)
|
223
|
+
code, data = 0, {}
|
224
|
+
fsize = fh.data_size
|
225
|
+
block_count = _block_count(fsize)
|
226
|
+
checksum_count = checksums.length
|
227
|
+
progress_count = progresses.length
|
228
|
+
if checksum_count != block_count || progress_count != block_count
|
229
|
+
raise BlockCountNotMathchError.new(fh.path, block_count, checksum_count, progress_count)
|
230
|
+
end
|
231
|
+
0.upto(block_count-1).each do |block_index|
|
232
|
+
if checksums[block_index].nil? || checksums[block_index].empty?
|
233
|
+
block_size = Config.settings[:block_size]
|
234
|
+
if block_index == block_count - 1
|
235
|
+
block_size = fsize - block_index*Config.settings[:block_size]
|
236
|
+
end
|
237
|
+
if progresses[block_index].nil?
|
238
|
+
progresses[block_index] = _new_block_put_progress_data
|
239
|
+
end
|
240
|
+
#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)
|
241
|
+
# Put the whole block as a chunk
|
242
|
+
code, data = _resumable_put_block(uptoken, fh, block_index, block_size, block_size, progresses[block_index], Config.settings[:max_retry_times], chunk_notifier)
|
243
|
+
if HTTP.is_response_ok?(code)
|
244
|
+
#checksums[block_index] = data["checksum"]
|
245
|
+
checksums[block_index] = data["ctx"]
|
246
|
+
if !block_notifier.nil? && block_notifier.respond_to?("notify")
|
247
|
+
block_notifier.notify(block_index, checksums[block_index])
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
return [code, data]
|
253
|
+
end # _resumable_put
|
254
|
+
|
255
|
+
def _mkfile(uphost,
|
256
|
+
uptoken,
|
257
|
+
entry_uri,
|
258
|
+
fsize,
|
259
|
+
checksums,
|
260
|
+
mime_type = nil,
|
261
|
+
custom_meta = nil,
|
262
|
+
customer = nil,
|
263
|
+
callback_params = nil,
|
264
|
+
rotate = nil)
|
265
|
+
path = '/rs-mkfile/' + Utils.urlsafe_base64_encode(entry_uri) + "/fsize/#{fsize}"
|
266
|
+
path += '/mimeType/' + Utils.urlsafe_base64_encode(mime_type) if !mime_type.nil? && !mime_type.empty?
|
267
|
+
path += '/meta/' + Utils.urlsafe_base64_encode(custom_meta) if !custom_meta.nil? && !custom_meta.empty?
|
268
|
+
path += '/customer/' + customer if !customer.nil? && !customer.empty?
|
269
|
+
callback_query_string = HTTP.generate_query_string(callback_params) if !callback_params.nil? && !callback_params.empty?
|
270
|
+
path += '/params/' + Utils.urlsafe_base64_encode(callback_query_string) if !callback_query_string.nil? && !callback_query_string.empty?
|
271
|
+
path += '/rotate/' + rotate if !rotate.nil? && rotate.to_i >= 0
|
272
|
+
url = uphost + path
|
273
|
+
#body = ''
|
274
|
+
#checksums.each do |checksum|
|
275
|
+
# body += Utils.urlsafe_base64_decode(checksum)
|
276
|
+
#end
|
277
|
+
body = checksums.join(',')
|
278
|
+
_call_binary_with_token(uptoken, url, body, 'text/plain')
|
279
|
+
end # _mkfile
|
280
|
+
|
281
|
+
def _resumable_upload(uptoken,
|
282
|
+
fh,
|
283
|
+
fsize,
|
284
|
+
bucket,
|
285
|
+
key,
|
286
|
+
mime_type = nil,
|
287
|
+
custom_meta = nil,
|
288
|
+
customer = nil,
|
289
|
+
callback_params = nil,
|
290
|
+
rotate = nil)
|
291
|
+
|
292
|
+
block_count = _block_count(fsize)
|
293
|
+
|
294
|
+
chunk_notifier = ChunkProgressNotifier.new()
|
295
|
+
block_notifier = BlockProgressNotifier.new()
|
296
|
+
|
297
|
+
progresses = []
|
298
|
+
block_count.times{progresses << _new_block_put_progress_data}
|
299
|
+
checksums = []
|
300
|
+
block_count.times{checksums << ''}
|
301
|
+
|
302
|
+
code, data, raw_headers = _resumable_put(uptoken, fh, checksums, progresses, block_notifier, chunk_notifier)
|
303
|
+
|
304
|
+
if HTTP.is_response_ok?(code)
|
305
|
+
uphost = data["host"]
|
306
|
+
entry_uri = bucket + ':' + key
|
307
|
+
code, data, raw_headers = _mkfile(uphost, uptoken, entry_uri, fsize, checksums, mime_type, custom_meta, customer, callback_params, rotate)
|
308
|
+
Utils.debug "Mkfile : #{code.inspect} #{data.inspect} #{raw_headers.inspect}"
|
309
|
+
end
|
310
|
+
|
311
|
+
if HTTP.is_response_ok?(code)
|
312
|
+
Utils.debug "File #{fh.path} {size: #{fsize}} successfully uploaded."
|
313
|
+
end
|
314
|
+
|
315
|
+
return code, data, raw_headers
|
316
|
+
end # _resumable_upload
|
317
|
+
end # self class
|
318
|
+
end # module Storage
|
319
|
+
end # module Qiniu
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'hmac-sha1'
|
4
|
+
require 'qiniu/config'
|
5
|
+
require 'qiniu/utils'
|
6
|
+
|
7
|
+
### AccessToken 类已经过时,请改用 Qiniu::Auth.generate_acctoken 方法 ###
|
8
|
+
|
9
|
+
module Qiniu
|
10
|
+
class AccessToken
|
11
|
+
|
12
|
+
include Utils
|
13
|
+
|
14
|
+
attr_accessor :access_key, :secret_key
|
15
|
+
|
16
|
+
def generate_encoded_digest(signature)
|
17
|
+
hmac = HMAC::SHA1.new(@secret_key)
|
18
|
+
hmac.update(signature)
|
19
|
+
urlsafe_base64_encode(hmac.digest)
|
20
|
+
end
|
21
|
+
|
22
|
+
end # AccessToken
|
23
|
+
end # module Qiniu
|