oss 0.1.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,318 @@
1
+ require 'oss/client'
2
+ require 'oss/util'
3
+ require 'openssl'
4
+ require 'base64'
5
+
6
+ module Oss
7
+
8
+ class Object
9
+
10
+ include Util
11
+
12
+ attr_accessor :client, :xml_obj
13
+
14
+ def initialize(client)
15
+ @client = client
16
+ end
17
+
18
+ def put_object(bucket_name, object, file, options = {})
19
+ # set header
20
+ headers = Util.hash_filter(options, {
21
+ expires: 'Expires',
22
+ content_control: 'Cache-Control',
23
+ content_encoding: 'Content-Encoding',
24
+ content_type: 'Content-Type',
25
+ content_md5: 'Content-MD5',
26
+ content_disposition: 'Content-Disposition',
27
+ encryption: 'x-oss-server-side-encryption',
28
+ acl: 'x-oss-object-acl',
29
+ })
30
+
31
+ # sign configs
32
+ sign_configs = {
33
+ resource: "/#{bucket_name}",
34
+ content_type: options[:content_type],
35
+ content_md5_check: options[:content_md5_check],
36
+ content_length_check: options[:content_length_check],
37
+ oss_headers: Util.oss_headers_to_s(options, {
38
+ acl: 'x-oss-object-acl',
39
+ encryption: 'x-oss-server-side-encryption',
40
+ })
41
+ }
42
+
43
+ client.put(
44
+ host: "#{bucket_name}.#{client.endpoint}",
45
+ path: "/#{object}",
46
+ headers: headers,
47
+ sign_configs: sign_configs,
48
+ payload: file,
49
+ )
50
+
51
+ true
52
+ end
53
+
54
+ def copy_object(bucket_name, object, old_bucket, old_object, options = {})
55
+ # copy source format
56
+ options[:copy_source] = "/#{old_bucket}/#{old_object}"
57
+
58
+ # set header
59
+ headers = Util.hash_filter(options, {
60
+ metadata_directive: 'x-oss-metadata-directive',
61
+ if_modified_since: 'x-oss-copy-source-if-modified-since',
62
+ if_unmodified_since: 'x-oss-copy-source-if-unmodified-since',
63
+ if_match: 'x-oss-copy-source-if-match',
64
+ if_none_match: 'x-oss-copy-source-if-none-match',
65
+ encryption: 'x-oss-server-side-encryption',
66
+ acl: 'x-oss-object-acl',
67
+ copy_source: 'x-oss-copy-source'
68
+ })
69
+
70
+ # sign configs
71
+ sign_configs = {
72
+ resource: "/#{bucket_name}",
73
+ content_type: 'application/x-www-form-urlencoded',
74
+ oss_headers: Util.oss_headers_to_s(options, {
75
+ metadata_directive: 'x-oss-metadata-directive',
76
+ if_modified_since: 'x-oss-copy-source-if-modified-since',
77
+ if_unmodified_since: 'x-oss-copy-source-if-unmodified-since',
78
+ if_match: 'x-oss-copy-source-if-match',
79
+ if_none_match: 'x-oss-copy-source-if-none-match',
80
+ encryption: 'x-oss-server-side-encryption',
81
+ acl: 'x-oss-object-acl',
82
+ copy_source: 'x-oss-copy-source'
83
+ })
84
+ }
85
+
86
+ @xml_obj = client.put(
87
+ host: "#{bucket_name}.#{client.endpoint}",
88
+ path: "/#{object}",
89
+ headers: headers,
90
+ sign_configs: sign_configs,
91
+ )
92
+
93
+ {
94
+ last_modify: @xml_obj.xpath('CopyObjectResult/LastModified').text,
95
+ etag: @xml_obj.xpath('CopyObjectResult/ETag').text,
96
+ }
97
+ end
98
+
99
+ def get_object(bucket_name, object, options = {})
100
+ # set header
101
+ headers = Util.hash_filter(options, {
102
+ range: 'Range',
103
+ if_modified_since: 'If-Modified-Since',
104
+ if_unmodified_since: 'If-Unmodified-Since',
105
+ if_match: 'If-Match',
106
+ if_none_match: 'If-None-Match'
107
+ })
108
+
109
+ # request params query string
110
+ query_string = Util.hash_filter(options, {
111
+ response_content_type: 'response-content-type',
112
+ response_content_language: 'response-content-language',
113
+ response_expires: 'response-expires',
114
+ response_cache_control: 'response-cache-control',
115
+ response_content_disposition: 'response-content-disposition',
116
+ response_content_encoding: 'response-content-encoding'
117
+ })
118
+
119
+ client.get(
120
+ host: "#{bucket_name}.#{client.endpoint}",
121
+ path: "/#{object}",
122
+ headers: headers,
123
+ sign_configs: { resource: "/#{bucket_name}", sign_query_string: true },
124
+ query_string: query_string,
125
+ as: :raw
126
+ )
127
+ end
128
+
129
+ def append_object(bucket_name, object, file, position = 0, options = {})
130
+ # set header
131
+ headers = Util.hash_filter(options, {
132
+ expires: 'Expires',
133
+ content_control: 'Cache-Control',
134
+ content_encoding: 'Content-Encoding',
135
+ content_type: 'Content-Type',
136
+ content_md5: 'Content-MD5',
137
+ content_disposition: 'Content-Disposition',
138
+ encryption: 'x-oss-server-side-encryption',
139
+ acl: 'x-oss-object-acl',
140
+ })
141
+
142
+ # sign configs
143
+ sign_configs = {
144
+ resource: "/#{bucket_name}",
145
+ content_type: options[:content_type],
146
+ content_md5: options[:content_md5],
147
+ sign_query_string: true,
148
+ oss_headers: Util.oss_headers_to_s(options, {
149
+ acl: 'x-oss-object-acl',
150
+ encryption: 'x-oss-server-side-encryption',
151
+ })
152
+ }
153
+
154
+ resp = client.post(
155
+ host: "#{bucket_name}.#{client.endpoint}",
156
+ path: "/#{object}?append&position=#{position}",
157
+ headers: headers,
158
+ sign_configs: sign_configs,
159
+ payload: file,
160
+ as: :raw
161
+ )
162
+
163
+ {
164
+ hash_crc64ecma: resp.headers[:x_oss_hash_crc64ecma],
165
+ next_append_position: resp.headers[:x_oss_next_append_position].to_i
166
+ }
167
+ end
168
+
169
+ # params:
170
+ # - bucket_name
171
+ # - object_name
172
+ def delete_object(bucket_name, object_name)
173
+
174
+ client.delete(
175
+ host: "#{bucket_name}.#{client.endpoint}",
176
+ path: "/#{object_name}",
177
+ sign_configs: {resource: "/#{bucket_name}"}
178
+ )
179
+
180
+ true
181
+ end
182
+
183
+ def delete_multiple_objects(bucket_name, objects = [])
184
+ # build payload xml
185
+ payload = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do
186
+ Delete do
187
+ Quiet 'false'
188
+ objects.each do |obj|
189
+ Object do
190
+ Key obj
191
+ end
192
+ end
193
+ end
194
+ end
195
+
196
+ xml_obj = client.post(
197
+ host: "#{bucket_name}.#{client.endpoint}",
198
+ path: "/?delete",
199
+ sign_configs: {resource: "/#{bucket_name}", content_type: 'application/x-www-form-urlencoded'},
200
+ content_md5_check: true,
201
+ content_length_check: true,
202
+ payload: payload.to_xml
203
+ )
204
+
205
+ # parse return deleted keys
206
+ deleted = Array.new
207
+ xml_obj.xpath('DeleteResult/Deleted').each do |obj|
208
+ deleted << obj.xpath('Key').text
209
+ end
210
+
211
+ deleted
212
+ end
213
+
214
+ def head_object(bucket_name, object_name, options = {})
215
+ # set header
216
+ headers = Util.hash_filter(options, {
217
+ if_modified_since: 'If-Modified-Since',
218
+ if_unmodified_since: 'If-Unmodified-Since',
219
+ if_match: 'If-Match',
220
+ if_none_match: 'If-None-Match'
221
+ })
222
+
223
+ resp = client.head(
224
+ host: "#{bucket_name}.#{client.endpoint}",
225
+ path: "/#{object_name}",
226
+ headers: headers,
227
+ sign_configs: {resource: "/#{bucket_name}"},
228
+ as: :raw
229
+ )
230
+
231
+ # head request returns hole response headers
232
+ resp.headers
233
+ end
234
+
235
+ # params:
236
+ # - bucket_name
237
+ # - object_name
238
+ # - acl
239
+ def put_object_acl(bucket_name, object_name, acl)
240
+ # sign configs
241
+ sign_configs = Hash.new
242
+ sign_configs[:resource] = "/#{bucket_name}"
243
+ sign_configs[:oss_headers] = "x-oss-object-acl:#{acl}"
244
+ sign_configs[:content_type] = 'application/x-www-form-urlencoded'
245
+
246
+ @xml_obj = client.put(
247
+ host: "#{bucket_name}.#{client.endpoint}",
248
+ path: "/#{object_name}?acl",
249
+ headers: {'x-oss-object-acl' => acl},
250
+ sign_configs: sign_configs
251
+ )
252
+
253
+ true
254
+ end
255
+
256
+ # params:
257
+ # - bucket_name
258
+ # - object_name
259
+ def get_object_acl(bucket_name, object_name)
260
+ xml_obj = client.get(
261
+ host: "#{bucket_name}.#{client.endpoint}",
262
+ path: "/#{object_name}?acl",
263
+ sign_configs: {resource: "/#{bucket_name}"}
264
+ ).xpath('AccessControlPolicy')
265
+
266
+ {
267
+ grant: xml_obj.xpath('AccessControlList/Grant').text,
268
+ owner: {
269
+ id: xml_obj.xpath('Owner/ID').text,
270
+ display_name: xml_obj.xpath('Owner/DisplayName').text
271
+ }
272
+ }
273
+ end
274
+
275
+ # params:
276
+ # - bucket_name
277
+ # - key
278
+ # - options
279
+ def post_object(bucket_name, key, options = {})
280
+
281
+ # build form upload info hash
282
+ form_hash = { 'key' => key, 'action' => "http://#{bucket_name}.#{client.endpoint}/"}
283
+ options.each do |k, v|
284
+ # need Signature
285
+ if k == :policy
286
+ # json policy string to base64 policy string
287
+ form_hash['policy'] = Base64.encode64(v).gsub("\n", '')
288
+
289
+ # create signature
290
+ digest = OpenSSL::Digest.new('sha1')
291
+ h = OpenSSL::HMAC.digest(digest, client.access_key_secret, form_hash['policy'])
292
+ # base64 result
293
+ form_hash['Signature'] = Base64.encode64(h).gsub("\n", '')
294
+ # add access key id
295
+ form_hash['OSSAccessKeyId'] = client.access_key_id
296
+ else
297
+ form_hash[k.to_s] = v
298
+ end
299
+ end
300
+
301
+ # return a hash for render a html upload form
302
+ form_hash
303
+ end
304
+
305
+ def method_missing(method)
306
+ if @xml_obj.nil?
307
+ super
308
+ else
309
+ camel = Util.camelize(method)
310
+ value = @xml_obj.xpath(camel)
311
+ raise "missing xml attribute #{camel}" if value.length == 0
312
+ value.inner_text
313
+ end
314
+ end
315
+
316
+ end
317
+
318
+ end
@@ -0,0 +1,63 @@
1
+ require 'oss/client'
2
+ require 'oss/util'
3
+
4
+ module Oss
5
+
6
+ SERVICE_HOST = 'oss.aliyuncs.com'
7
+
8
+ class Service
9
+
10
+ include Util
11
+
12
+ attr_accessor :client, :xml_obj
13
+
14
+ def initialize(client)
15
+ @client = client
16
+ end
17
+
18
+ # params:
19
+ # - options:
20
+ # - prefix
21
+ # - marker
22
+ # - max_keys
23
+ def get_service(options = {})
24
+ @xml_obj = client.get(host: SERVICE_HOST, path: '/', query_string: options).xpath('ListAllMyBucketsResult')
25
+ self
26
+ end
27
+
28
+ def owner
29
+ owner = @xml_obj.xpath('Owner')
30
+ {
31
+ :id => owner.xpath('ID').text,
32
+ :display_name => owner.xpath('DisplayName').text
33
+ }
34
+ end
35
+
36
+ def buckets
37
+ buckets = @xml_obj.xpath('Buckets')
38
+ results = Array.new
39
+ buckets.each do |bucket|
40
+ results << {
41
+ :location => bucket.xpath('Bucket/Location').text,
42
+ :name => bucket.xpath('Bucket/Name').text,
43
+ :creation_date => bucket.xpath('Bucket/CreationDate').text
44
+ }
45
+ end
46
+ results
47
+ end
48
+
49
+ # prefix, marker, max_keys, is_truncated, next_marker
50
+ def method_missing(method)
51
+ if @xml_obj.nil?
52
+ super
53
+ else
54
+ camel = Util.camelize(method)
55
+ value = @xml_obj.xpath(camel)
56
+ raise "missing xml attribute #{camel}" if value.length == 0
57
+ value.inner_text
58
+ end
59
+ end
60
+
61
+ end
62
+
63
+ end
@@ -0,0 +1,59 @@
1
+ module Oss
2
+
3
+ module Util extend self
4
+
5
+ def camelize(str)
6
+ str.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
7
+ end
8
+
9
+ def hash_filter(input_hash, key_map, target_hash = Hash.new)
10
+ key_map.each do |key, value|
11
+ unless input_hash[key].nil?
12
+ target_hash[value] = input_hash[key]
13
+ end
14
+ end
15
+ target_hash
16
+ end
17
+
18
+ # x-oss:value
19
+ def oss_headers_to_s(input_hash, key_map)
20
+ header_array = Array.new
21
+
22
+ # sort by hash value
23
+ sorted = Hash[key_map.sort_by{|k,v| v}]
24
+
25
+ sorted.each do |key, value|
26
+ unless input_hash[key].nil?
27
+ header_array << "#{value}:#{input_hash[key]}"
28
+ end
29
+ end
30
+ header_array.join("\n")
31
+ end
32
+
33
+ def set_query_string(path, query)
34
+ return path if query.nil?
35
+
36
+ attrs = Array.new
37
+ new_path = "#{path}"
38
+
39
+ # sort by hash value
40
+ sorted = Hash[query.sort_by{|k,v| k}]
41
+
42
+ sorted.each do |k, v|
43
+ # query key ruby hash _ to -
44
+ attrs << "#{k.to_s.gsub('_', '-')}=#{v}"
45
+ end
46
+
47
+ # create http request query string
48
+ if attrs.length > 0
49
+ new_path << "?#{attrs.join('&')}"
50
+ end
51
+
52
+ new_path
53
+ end
54
+
55
+
56
+
57
+ end
58
+
59
+ end
@@ -0,0 +1,3 @@
1
+ module Oss
2
+ VERSION = '0.1.1'
3
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'oss/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "oss"
8
+ spec.version = Oss::VERSION
9
+ spec.authors = ["RaymondChou"]
10
+ spec.email = ["freezestart@gmail.com"]
11
+
12
+ spec.summary = %q{Aliyun OSS Ruby SDK}
13
+ spec.description = %q{Aliyun OSS(Object Storage Service) Ruby SDK}
14
+ spec.homepage = "http://github.com/RaymondChou"
15
+ spec.license = "Apache 2.0"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.10"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency "rest-client", "~> 1.8"
25
+ spec.add_development_dependency "nokogiri", "~> 1.6"
26
+ end