aliyun-oss-sdk 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +17 -0
  3. data/Gemfile +1 -0
  4. data/README.md +12 -10
  5. data/Rakefile +10 -0
  6. data/aliyun-oss.gemspec +2 -2
  7. data/lib/aliyun/oss.rb +4 -11
  8. data/lib/aliyun/oss/authorization.rb +59 -27
  9. data/lib/aliyun/oss/client.rb +137 -62
  10. data/lib/aliyun/oss/client/bucket_multiparts.rb +43 -0
  11. data/lib/aliyun/oss/client/bucket_objects.rb +111 -0
  12. data/lib/aliyun/oss/client/buckets.rb +50 -0
  13. data/lib/aliyun/oss/client/clients.rb +54 -0
  14. data/lib/aliyun/oss/error.rb +43 -0
  15. data/lib/aliyun/oss/http.rb +65 -23
  16. data/lib/aliyun/oss/struct.rb +26 -0
  17. data/lib/aliyun/oss/struct/bucket.rb +252 -0
  18. data/lib/aliyun/oss/struct/cors.rb +65 -0
  19. data/lib/aliyun/oss/struct/lifecycle.rb +73 -0
  20. data/lib/aliyun/oss/struct/logging.rb +33 -0
  21. data/lib/aliyun/oss/struct/multipart.rb +101 -0
  22. data/lib/aliyun/oss/struct/object.rb +71 -0
  23. data/lib/aliyun/oss/struct/part.rb +48 -0
  24. data/lib/aliyun/oss/struct/referer.rb +21 -0
  25. data/lib/aliyun/oss/struct/website.rb +13 -0
  26. data/lib/aliyun/oss/utils.rb +41 -1
  27. data/lib/aliyun/oss/version.rb +1 -1
  28. data/lib/aliyun/oss/xml_generator.rb +174 -0
  29. data/todo.md +5 -0
  30. data/wiki/bucket.md +20 -20
  31. data/wiki/cors.md +59 -0
  32. data/wiki/error.md +42 -31
  33. data/wiki/get_start.md +24 -7
  34. data/wiki/installation.md +2 -2
  35. data/wiki/lifecycle.md +75 -0
  36. data/wiki/multipart.md +7 -5
  37. data/wiki/object.md +16 -12
  38. metadata +48 -6
  39. data/lib/aliyun/oss/multipart/part.rb +0 -32
  40. data/lib/aliyun/oss/rule/cors.rb +0 -63
  41. data/lib/aliyun/oss/rule/lifecycle.rb +0 -61
@@ -0,0 +1,71 @@
1
+ module Aliyun
2
+ module Oss
3
+ module Struct
4
+ class Object < Base
5
+ # Key of object
6
+ attr_accessor :key
7
+
8
+ # last modified time of object
9
+ attr_accessor :last_modified
10
+
11
+ # etag of object
12
+ attr_accessor :etag
13
+
14
+ # type of object
15
+ attr_accessor :type
16
+
17
+ # size of object
18
+ attr_accessor :size
19
+
20
+ # storage class of object
21
+ attr_accessor :storage_class
22
+
23
+ # owner of object
24
+ attr_accessor :owner
25
+
26
+ # location of object
27
+ attr_accessor :location
28
+
29
+ # bucket of object placed
30
+ attr_accessor :bucket
31
+
32
+ # reference to client
33
+ attr_accessor :client
34
+
35
+ # Get ACL for object
36
+ #
37
+ # @raise [RequestError]
38
+ #
39
+ # @return [String]
40
+ def acl!
41
+ result = client.bucket_get_object_acl(key).parsed_response
42
+ acl_keys = %w(AccessControlPolicy AccessControlList Grant)
43
+ Utils.dig_value(result, *acl_keys).strip
44
+ end
45
+
46
+ # Set ACL for object
47
+ #
48
+ # @param acl [String] access value, supported value: private, public-read, public-read-write
49
+ #
50
+ # @raise [RequestError]
51
+ #
52
+ # @return [true]
53
+ def set_acl(acl)
54
+ !!client.bucket_set_object_acl(key, acl)
55
+ end
56
+
57
+ # Get meta information of object
58
+ #
59
+ # @param headers [Hash] headers
60
+ # @option (see #bucket_get_meta_object)
61
+ #
62
+ # @raise [RequestError]
63
+ #
64
+ # @return [Hash]
65
+ def meta!(*args)
66
+ client.bucket_get_meta_object(*args.unshift(key)).headers
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,48 @@
1
+ module Aliyun
2
+ module Oss
3
+ module Struct
4
+ class Part < Base
5
+ # [Integer] :number the part number
6
+ attr_accessor :number
7
+
8
+ # [String] :etag the etag for the part
9
+ attr_accessor :etag
10
+
11
+ # Last Modified time
12
+ attr_accessor :last_modified
13
+
14
+ # Part size
15
+ attr_accessor :size
16
+
17
+ def last_modified=(last_modified)
18
+ @last_modified = Time.parse(last_modified)
19
+ end
20
+
21
+ def part_number=(part_number)
22
+ @number = part_number
23
+ end
24
+
25
+ def e_tag=(e_tag)
26
+ @etag = e_tag
27
+ end
28
+
29
+ def to_hash
30
+ if valid?
31
+ {
32
+ 'PartNumber' => number,
33
+ 'ETag' => etag
34
+ }
35
+ else
36
+ {}
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def valid?
43
+ number && etag
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,21 @@
1
+ module Aliyun
2
+ module Oss
3
+ module Struct
4
+ class Referer < Base
5
+ # specify allow empty referer access
6
+ attr_accessor :allow_empty
7
+
8
+ # specify white list for allows referers
9
+ attr_accessor :referers
10
+
11
+ def allow_empty=(allow_empty)
12
+ @allow_empty = allow_empty == 'true'
13
+ end
14
+
15
+ def referers=(referers)
16
+ @referers = Utils.wrap(referers).map(&:strip)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,13 @@
1
+ module Aliyun
2
+ module Oss
3
+ module Struct
4
+ class Website < Base
5
+ # A suffix that is appended to a request that is for a directory on the website endpoint (e.g. if the suffix is index.html and you make a request to samplebucket/images/ the data that is returned will be for the object with the key name images/index.html) The suffix must not be empty and must not include a slash character.
6
+ attr_accessor :suffix
7
+
8
+ # The object key name to use when a 4XX class error occurs
9
+ attr_accessor :error_key
10
+ end
11
+ end
12
+ end
13
+ end
@@ -43,7 +43,47 @@ module Aliyun
43
43
  end
44
44
 
45
45
  def self.to_xml(hash) # nodoc
46
- %|<?xml version="1.0" encoding="UTF-8"?>#{Gyoku.xml(hash)}|
46
+ %(<?xml version="1.0" encoding="UTF-8"?>#{Gyoku.xml(hash)})
47
+ end
48
+
49
+ # Dig values in deep hash
50
+ #
51
+ # @example
52
+ # dig_value({ 'a' => { 'b' => { 'c' => 3 } } }, 'a', 'b', 'c') # => 3
53
+ #
54
+ def self.dig_value(hash, *keys)
55
+ new_hash = hash.dup
56
+
57
+ keys.each do |key|
58
+ if new_hash.is_a?(Hash) && new_hash.key?(key)
59
+ new_hash = new_hash[key]
60
+ else
61
+ return nil
62
+ end
63
+ end
64
+ new_hash
65
+ end
66
+
67
+ # @see {http://apidock.com/rails/String/underscore String#underscore}
68
+ def self.underscore(str)
69
+ word = str.to_s.dup
70
+ word.gsub!(/::/, '/')
71
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
72
+ word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
73
+ word.tr!('-', '_')
74
+ word.downcase!
75
+ word
76
+ end
77
+
78
+ # Copy from {https://github.com/rails/rails/blob/14254d82a90b8aa4bd81f7eeebe33885bf83c378/activesupport/lib/active_support/core_ext/array/wrap.rb#L36 ActiveSupport::Array#wrap}
79
+ def self.wrap(object)
80
+ if object.nil?
81
+ []
82
+ elsif object.respond_to?(:to_ary)
83
+ object.to_ary || [object]
84
+ else
85
+ [object]
86
+ end
47
87
  end
48
88
  end
49
89
  end
@@ -1,5 +1,5 @@
1
1
  module Aliyun
2
2
  module Oss
3
- VERSION = '0.0.3'
3
+ VERSION = '0.1.0'
4
4
  end
5
5
  end
@@ -0,0 +1,174 @@
1
+ module Aliyun
2
+ module Oss
3
+ class XmlGenerator
4
+ # Generate xml from rules
5
+ #
6
+ # @example
7
+ # <?xml version="1.0" encoding="UTF-8"?>
8
+ # <LifecycleConfiguration>
9
+ # <Rule>
10
+ # <ID>RuleID</ID>
11
+ # <Prefix>Prefix</Prefix>
12
+ # <Status>Status</Status>
13
+ # <Expiration>
14
+ # <Days>Days</Days>
15
+ # </Expiration>
16
+ # </Rule>
17
+ # </LifecycleConfiguration>
18
+ def self.generate_lifecycle_rules(rules)
19
+ Utils.to_xml(
20
+ 'LifecycleConfiguration' => {
21
+ 'Rule' => rules.map(&:to_hash)
22
+ })
23
+ end
24
+
25
+ # Generate xml for cors from rules
26
+ #
27
+ # @example
28
+ # <?xml version="1.0" encoding="UTF-8"?>
29
+ # <CORSConfiguration>
30
+ # <CORSRule>
31
+ # <AllowedOrigin>the origin you want allow CORS request from</AllowedOrigin>
32
+ # <AllowedOrigin>...AllowedOrigin>
33
+ # <AllowedMethod>HTTP method</AllowedMethod>
34
+ # <AllowedMethod>...AllowedMethod>
35
+ # <AllowedHeader> headers that allowed browser to send</AllowedHeader>
36
+ # <AllowedHeader>...AllowedHeader>
37
+ # <ExposeHeader> headers in response that can access from client app</ExposeHeader>
38
+ # <ExposeHeader>...ExposeHeader>
39
+ # <MaxAgeSeconds>time to cache pre-fight response</MaxAgeSeconds>
40
+ # </CORSRule>
41
+ # <CORSRule>
42
+ # ...
43
+ # </CORSRule>
44
+ # ...
45
+ # </CORSConfiguration >
46
+ #
47
+ def self.generate_cors_rules(rules)
48
+ Utils.to_xml(
49
+ 'CORSConfiguration' => {
50
+ 'CORSRule' => rules.map(&:to_hash)
51
+ })
52
+ end
53
+
54
+ # Generate xml for delete objects
55
+ #
56
+ # @example
57
+ # <?xml version="1.0" encoding="UTF-8"?>
58
+ # <Delete>
59
+ # <Quiet>true</Quiet>
60
+ # <Object>
61
+ # <Key>key</Key>
62
+ # </Object>
63
+ # ...
64
+ # </Delete>
65
+ #
66
+ def self.generate_delete_objects_xml(keys, quiet)
67
+ key_objects = keys.map { |key| { 'Key' => key } }
68
+ Utils.to_xml(
69
+ 'Delete' => {
70
+ 'Object' => key_objects,
71
+ 'Quiet' => quiet
72
+ })
73
+ end
74
+
75
+ # Generate xml for complete multipart from parts
76
+ #
77
+ # @example
78
+ # <CompleteMultipartUpload>
79
+ # <Part>
80
+ # <PartNumber>PartNumber</PartNumber>
81
+ # <ETag>ETag</ETag>
82
+ # </Part>
83
+ # ...
84
+ # </CompleteMultipartUpload>
85
+ #
86
+ def self.generate_complete_multipart_xml(parts)
87
+ Utils.to_xml(
88
+ 'CompleteMultipartUpload' => {
89
+ 'Part' => parts.map(&:to_hash)
90
+ })
91
+ end
92
+
93
+ # Generate xml for #bucket_create with location
94
+ #
95
+ # @example
96
+ # <?xml version="1.0" encoding="UTF-8"?>
97
+ # <CreateBucketConfiguration>
98
+ # <LocationConstraint>BucketRegion</LocationConstraint>
99
+ # </CreateBucketConfiguration>
100
+ #
101
+ def self.generate_create_bucket_xml(location)
102
+ configuration = {
103
+ 'CreateBucketConfiguration' => {
104
+ 'LocationConstraint' => location
105
+ }
106
+ }
107
+ Utils.to_xml(configuration)
108
+ end
109
+
110
+ # Generate xml for enable logging
111
+ #
112
+ # @example
113
+ # <?xml version="1.0" encoding="UTF-8"?>
114
+ # <BucketLoggingStatus>
115
+ # <LoggingEnabled>
116
+ # <TargetBucket>TargetBucket</TargetBucket>
117
+ # <TargetPrefix>TargetPrefix</TargetPrefix>
118
+ # </LoggingEnabled>
119
+ # </BucketLoggingStatus>
120
+ #
121
+ def self.generate_enable_logging_xml(target_bucket, target_prefix)
122
+ logging = { 'TargetBucket' => target_bucket }
123
+ logging.merge!('TargetPrefix' => target_prefix) if target_prefix
124
+ Utils.to_xml(
125
+ 'BucketLoggingStatus' => {
126
+ 'LoggingEnabled' => logging
127
+ })
128
+ end
129
+
130
+ # Generate xml for enable website
131
+ #
132
+ # @example
133
+ # <?xml version="1.0" encoding="UTF-8"?>
134
+ # <WebsiteConfiguration>
135
+ # <IndexDocument>
136
+ # <Suffix>index.html</Suffix>
137
+ # </IndexDocument>
138
+ # <ErrorDocument>
139
+ # <Key>errorDocument.html</Key>
140
+ # </ErrorDocument>
141
+ # </WebsiteConfiguration>
142
+ #
143
+ def self.generate_enable_website_xml(suffix, key)
144
+ website_configuration = { 'IndexDocument' => { 'Suffix' => suffix } }
145
+ website_configuration.merge!('ErrorDocument' => { 'Key' => key }) if key
146
+ Utils.to_xml('WebsiteConfiguration' => website_configuration)
147
+ end
148
+
149
+ # Generate xml for set referer
150
+ #
151
+ # @example
152
+ # <?xml version="1.0" encoding="UTF-8"?>
153
+ # <RefererConfiguration>
154
+ # <AllowEmptyReferer>true</AllowEmptyReferer >
155
+ # <RefererList>
156
+ # <Referer> http://www.aliyun.com</Referer>
157
+ # <Referer> https://www.aliyun.com</Referer>
158
+ # <Referer> http://www.*.com</Referer>
159
+ # <Referer> https://www.?.aliyuncs.com</Referer>
160
+ # </ RefererList>
161
+ # </RefererConfiguration>
162
+ #
163
+ def self.generate_set_referer_xml(referers, allowed_empty)
164
+ Utils.to_xml(
165
+ 'RefererConfiguration' => {
166
+ 'AllowEmptyReferer' => allowed_empty,
167
+ 'RefererList' => {
168
+ 'Referer' => referers
169
+ }
170
+ })
171
+ end
172
+ end
173
+ end
174
+ end
data/todo.md ADDED
@@ -0,0 +1,5 @@
1
+ ## TODO
2
+
3
+ + Command Line Tool
4
+ + Custom Domain Support
5
+ + Get Download URL
@@ -1,10 +1,10 @@
1
1
  ## Bucket
2
2
 
3
- Bucket is namespace in OSS, as well as management entity for high functions such as pricing, access control, logging; Bucket names are global uniqueness throughout the OSS services, and cannot be modified. Each Object stored in the OSS must contained in a Bucket. An application, such as the picture sharing website, can correspond to one or more Bucket. A user can create up to 10 Bucket, but each bucket can store unlimit objects, there is no limit to the number of storage capacity each buckte highest support 2 PB.
3
+ Bucket is a namespace in OSS, as well as management entity for high functions such as pricing, access control, logging; Bucket names are global uniqueness throughout the OSS services, and cannot be modified. Each Object stored in the OSS must contained in a Bucket. An application, such as the picture sharing website, can correspond to one or more Bucket. A user can create up to 10 Bucket, but each bucket can store unlimit objects, there is no limit to the number of storage capacity each buckte highest support 2 PB.
4
4
 
5
5
  ### Name Spec
6
6
 
7
- + only contains lowercase letters, Numbers, dash (-)
7
+ + Only contains lowercase letters, Numbers, dash (-)
8
8
  + Must begin with lowercase letters or Numbers
9
9
  + Length must be between 3-63 bytes
10
10
 
@@ -21,7 +21,8 @@ Bucket is namespace in OSS, as well as management entity for high functions such
21
21
  res = client.bucket_create('new-bucket', 'oss-cn-beijing', 'private')
22
22
  puts res.success?, res.headers
23
23
 
24
- You can specify bucket name, location and acl when create new bucket. More about this method, visit [Client#bucket_create]()
24
+ You can specify bucket name, location(default 'oss-cn-hangzhou') and acl(default: 'private') when create new bucket.
25
+
25
26
 
26
27
  ### List all buckets
27
28
 
@@ -35,40 +36,38 @@ To get all buckets use Client#list_buckets:
35
36
  client = Aliyun::Oss::Client.new(access_key, secret_key, host: host)
36
37
 
37
38
  res = client.list_buckets
38
- puts res.parsed_response
39
+ puts res.success?, res.parsed_response
39
40
 
40
41
 
41
- ### Set ACL for Bucket
42
+ ### Set ACL
42
43
 
43
- With Client#bucket_set_acl you can modify the ACL for bucket:
44
+ With Client#bucket_set_acl you can modify the ACL:
44
45
 
45
46
  require 'aliyun/oss'
46
47
 
47
48
  access_key, secret_key = "your id", "your secret"
48
- host = "oss-cn-hangzhou.aliyuncs.com"
49
- bucket = "bucket-name"
49
+ host, bucket = "oss-cn-hangzhou.aliyuncs.com", "bucket-name"
50
50
  client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket)
51
51
 
52
52
  # supported value: public-read-write | public-read | private
53
53
  res = client.bucket_set_acl("public-read")
54
- puts res.parsed_response
54
+ puts res.success?, res.parsed_response
55
55
 
56
56
  Now, it support public-read-write | public-read | private, more detail visit: [Bucket ACL](https://docs.aliyun.com/#/pub/oss/product-documentation/acl&bucket-acl)
57
57
 
58
- ### Get ACL of Bucket
59
58
 
60
- To get current ACL of Bucket, use Client#bucket_get_acl
59
+ ### Get ACL
61
60
 
61
+ To get current ACL of Bucket, use Client#bucket_get_acl:
62
62
 
63
63
  require 'aliyun/oss'
64
64
 
65
65
  access_key, secret_key = "your id", "your secret"
66
- host = "oss-cn-hangzhou.aliyuncs.com"
67
- bucket = "bucket-name"
66
+ host, bucket = "oss-cn-hangzhou.aliyuncs.com", "bucket-name"
68
67
  client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket)
69
68
 
70
69
  res = client.bucket_get_acl
71
- puts res.parsed_response
70
+ puts res.success?, res.parsed_response
72
71
 
73
72
 
74
73
  ### Get Bucket Location
@@ -78,18 +77,18 @@ Get bucket's data center location, use Client#bucket_get_location:
78
77
  require 'aliyun/oss'
79
78
 
80
79
  access_key, secret_key = "your id", "your secret"
81
- host = "oss-cn-hangzhou.aliyuncs.com"
82
- bucket = "bucket-name"
80
+ host, bucket = "oss-cn-hangzhou.aliyuncs.com", "bucket-name"
83
81
  client = Aliyun::Oss::Client.new(access_key, secret_key, host: host, bucket: bucket)
84
82
 
85
83
  res = client.bucket_get_location
86
- puts res.parsed_response
84
+ puts res.success?, res.parsed_response
85
+
86
+ To get more bucket information, visit Client#bucket_get_xxx methods [here](http://www.rubydoc.info/gems/aliyun-oss-sdk/Aliyun/Oss/Client).
87
87
 
88
- More bucket information can fetch via Client#bucket_get_xxx methods. View the [Ruby Document]()
89
88
 
90
89
  ### Delete Bucket
91
90
 
92
- If you donot need the bucket, delete it with Client#bucket_delete
91
+ If you do need one bucket, delete it with Client#bucket_delete:
93
92
 
94
93
  require 'aliyun/oss'
95
94
 
@@ -100,7 +99,8 @@ If you donot need the bucket, delete it with Client#bucket_delete
100
99
  res = client.bucket_delete("deleted-bucket-name")
101
100
  puts res.success?, res.headers
102
101
 
103
- Note: when the bucket is not empty(existing object or parts upload via Multipart), the delete may be fail.
102
+ Note: when the bucket is not empty(existing object or [Multipart Uploaded](./multipart.md) parts), the delete will fail.
103
+
104
104
 
105
105
  OK, Let's visit [Objects](./object.md)
106
106