fog-aliyun 0.3.7 → 0.3.12

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 71f324e607c567e9377a324698f35edd82667312c28fce1d3d8dc2e7e771af19
4
- data.tar.gz: 608ea19f6289e5fc3e6ab8642f1326b7b88cb81639853b91f98be251923633e5
3
+ metadata.gz: c47b8a9b6a56d553d1b0161be29b2d2444671f41f083e8af34707bd1eadc1718
4
+ data.tar.gz: 17e83f9d11b7ae7b1802941ddb13427346bbd971b563d7ac04436f7d036d7ef7
5
5
  SHA512:
6
- metadata.gz: 80f63cc617a5e1d1758955342ae252c94e89dbb429575ad8da406100d3dd56a446dd34cbfa896be2c8908e7fd118e7cfb6e87f648def81a52b812c1f72832be8
7
- data.tar.gz: 1da3cd3a1a3d8bc19d2290fdce54f2e9efe1e26849a93a8a5a2b7310f3303c46e1550d0e445cd316b1a7a8fdd5c39f2a07f79a19351e3857b02bd6cf117df37d
6
+ metadata.gz: 52060f2e1a3881ccb72ccc6e5fbeddd988bd2820416c419a7409e714c6d4f08fb7b37f5f98ff1e8fc66b47c0f2aa5656349da06e939edfb0c78c4adba05bf1ef
7
+ data.tar.gz: 30bdcf81e3e78e2983545d391ef2062ebc49975bee90c9d93226f30a631b3eaa7627916b179d15c684b38211bbd2e9e78e84210a2471b8185c5c3d21fbfe3001
data/.gitignore CHANGED
@@ -23,3 +23,5 @@ tmp
23
23
  *.a
24
24
  mkmf.log
25
25
  gemfiles/*.lock
26
+ morethan100m
27
+ *.log
data/README.md CHANGED
@@ -30,9 +30,9 @@ Since it's a bad practice to have your credentials in source code, you should lo
30
30
 
31
31
  ```
32
32
  default:
33
- :aliyun_accesskey_id: <YOUR_ACCESS_KEY_ID>,
34
- :aliyun_accesskey_secret: <YOUR_SECRET_ACCESS_KEY>,
35
- :aliyun_region_id: <YOUR_TARGET_REGION>
33
+ aliyun_accesskey_id: "<YOUR_ACCESS_KEY_ID>"
34
+ aliyun_accesskey_secret: "<YOUR_SECRET_ACCESS_KEY>"
35
+ aliyun_region_id: "<YOUR_TARGET_REGION>"
36
36
  ```
37
37
 
38
38
  ### Connecting to OSS
@@ -307,6 +307,64 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
307
307
 
308
308
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
309
309
 
310
+ ## Testing
311
+
312
+
313
+
314
+ To run test suite use the following command:
315
+
316
+ ```
317
+ rake spec
318
+ ```
319
+
320
+ ### Code coverage
321
+
322
+ To run test suite with code coverage:
323
+
324
+ ```
325
+ export COVERAGE=true
326
+ rake spec
327
+ ```
328
+
329
+ The result will be generated in `coverage` folder.
330
+
331
+ ### Integration tests
332
+
333
+ To run integration tests please prepare a set of AliCloud credentials to be used by integration tests.
334
+
335
+ Define the credentials and bucket in `~/.fog` file in using following format:
336
+
337
+ ```
338
+ default:
339
+ aliyun_accesskey_id: "...access key..." # You can create a set of credentials
340
+ aliyun_accesskey_secret: "...secret..." # using Alicloud console portal
341
+ aliyun_region_id: "...region name..." # Example: cn-shanghai
342
+ aliyun_oss_bucket: "...name of the bucket..." # Example: fog-integration-test-bucket
343
+ ```
344
+
345
+ WARNING: Do NOT use any productive account credentials and buckets for the testing, it may be harmful to your data!
346
+
347
+ The tests are using [https://github.com/aliyun/aliyun-cli#installation](Aliyun CLI) to setup integration bucket and content for tests,
348
+ please install it locally before running integration tests.
349
+
350
+ Aliyun CLI will be configured automatically as part of test execution using the credentials provided for fog connection.
351
+
352
+ Then run the test suite with `INTEGRATION` environment variable to activate integration tests:
353
+
354
+ ```
355
+ export INTEGRATION=true
356
+ rake spec
357
+ ```
358
+
359
+ ### Performance test
360
+
361
+ Performance tests are providing memory consumption report for download/upload operations.
362
+
363
+ ```
364
+ export PERFORMANCE=true
365
+ rake spec
366
+ ```
367
+
310
368
  ## License
311
369
 
312
370
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -7,10 +7,10 @@ require 'fog/aliyun/version'
7
7
  Gem::Specification.new do |spec|
8
8
  spec.name = 'fog-aliyun'
9
9
  spec.version = Fog::Aliyun::VERSION
10
- spec.authors = ['Qinsi Deng, Jianxun Li, Jane Han']
11
- spec.email = ['dengqinsi@sina.com']
10
+ spec.authors = ['Qinsi Deng, Jianxun Li, Jane Han, Guimin He']
11
+ spec.email = ['dengqinsi@sina.com', 'guimin.hgm@alibaba-inc.com']
12
12
 
13
- spec.summary = 'Fog provider for Aliyun Web Services.'
13
+ spec.summary = 'Fog provider for Alibaba Cloud Web Services.'
14
14
  spec.description = 'As a FOG provider, fog-aliyun support aliyun OSS/ECS. It will support more aliyun services later.'
15
15
  spec.homepage = 'https://github.com/fog/fog-aliyun'
16
16
  spec.license = 'MIT'
@@ -26,9 +26,13 @@ Gem::Specification.new do |spec|
26
26
  spec.add_development_dependency 'rake'
27
27
  spec.add_development_dependency 'rspec'
28
28
  spec.add_development_dependency 'rubocop'
29
+ spec.add_development_dependency 'simplecov'
30
+ spec.add_development_dependency 'memory_profiler'
31
+ spec.add_development_dependency 'aliyun-sdk'
29
32
 
30
33
  spec.add_dependency 'fog-core'
31
34
  spec.add_dependency 'fog-json'
32
35
  spec.add_dependency 'ipaddress', '~> 0.8'
33
36
  spec.add_dependency 'xml-simple', '~> 1.1'
37
+ spec.add_dependency 'aliyun-sdk'
34
38
  end
@@ -2,19 +2,16 @@
2
2
 
3
3
  require 'fog/core'
4
4
  require 'fog/json'
5
- require 'fog/aliyun/version'
5
+ require File.expand_path('../aliyun/version', __FILE__)
6
6
 
7
7
  module Fog
8
- module Compute
9
- autoload :Aliyun, 'fog/aliyun/compute'
10
- end
11
-
12
- module Storage
13
- autoload :Aliyun, 'fog/aliyun/storage'
14
- end
15
-
16
8
  module Aliyun
17
9
  extend Fog::Provider
10
+
11
+ # Services
12
+ autoload :Compute, File.expand_path('../aliyun/compute', __FILE__)
13
+ autoload :Storage, File.expand_path('../aliyun/storage', __FILE__)
14
+
18
15
  service(:compute, 'Compute')
19
16
  service(:storage, 'Storage')
20
17
  end
@@ -4,10 +4,10 @@ require 'fog/core/collection'
4
4
  require 'fog/aliyun/models/storage/directory'
5
5
 
6
6
  module Fog
7
- module Storage
8
- class Aliyun
7
+ module Aliyun
8
+ class Storage
9
9
  class Directories < Fog::Collection
10
- model Fog::Storage::Aliyun::Directory
10
+ model Fog::Aliyun::Storage::Directory
11
11
 
12
12
  def all
13
13
  containers = service.get_containers
@@ -30,6 +30,9 @@ module Fog
30
30
  # If key is a directory(including /), return an existed or a new one;
31
31
  # If key does not contain /, if bucket, return '', else return an existed or a new one directory;
32
32
  def get(key, options = {})
33
+ if key.is_a? Array
34
+ key = key[0]
35
+ end
33
36
  if !key.nil? && key != '' && key != '.'
34
37
  key = key.chomp('/')
35
38
  if key.include? '/'
@@ -37,21 +40,39 @@ module Fog
37
40
  ret = service.head_object(dir, options)
38
41
  new(key: key) if ret.data[:status] == 200
39
42
  else
40
- data = service.get_bucket(key)
41
- puts "[DEBUG] Getting the bucket named with #{key}..."
42
- puts data
43
- if data.class == Hash && data.key?('Code') && !data['Code'].nil? && !data['Code'].empty?
44
- dir = key + '/'
45
- ret = service.head_object(dir, options)
46
- new(key: key) if ret.data[:status] == 200
47
- else
48
- new(key: '')
43
+ remap_attributes(options, {
44
+ :delimiter => 'delimiter',
45
+ :marker => 'marker',
46
+ :max_keys => 'max-keys',
47
+ :prefix => 'prefix'
48
+ })
49
+ data = service.get_bucket(key, options)
50
+ directory = new(:key => data['Name'], :is_persisted => true)
51
+ options = {}
52
+ for k, v in data
53
+ if ['CommonPrefixes', 'Delimiter', 'IsTruncated', 'Marker', 'MaxKeys', 'Prefix'].include?(k)
54
+ # Sometimes, the v will be a Array, like "Name"=>["blobstore-droplet1"], "Prefix"=>[{}], "Marker"=>[{}], "MaxKeys"=>["100"], "Delimiter"=>[{}], "IsTruncated"=>["false"]
55
+ # and there needs to parse them
56
+ if !v.nil? && (v.is_a? Array) && (v.size > 0)
57
+ if v[0].is_a? Hash
58
+ v = nil
59
+ else
60
+ v = v[0]
61
+ end
62
+ end
63
+ options[k] = v
64
+ end
65
+ end
66
+ directory.files.merge_attributes(options)
67
+ if data.key?('Contents') && !data['Contents'].nil?
68
+ directory.files.load(data['Contents'])
49
69
  end
70
+ directory
50
71
  end
51
72
  else
52
73
  new(key: '')
53
74
  end
54
- rescue Fog::Storage::Aliyun::NotFound
75
+ rescue Fog::Aliyun::Storage::NotFound
55
76
  nil
56
77
  end
57
78
  end
@@ -4,10 +4,10 @@ require 'fog/core/model'
4
4
  require 'fog/aliyun/models/storage/files'
5
5
 
6
6
  module Fog
7
- module Storage
8
- class Aliyun
7
+ module Aliyun
8
+ class Storage
9
9
  class Directory < Fog::Model
10
- identity :key
10
+ identity :key, :aliases => ['Key', 'Name', 'name']
11
11
 
12
12
  def destroy
13
13
  requires :key
@@ -15,20 +15,19 @@ module Fog
15
15
  ret = service.list_objects(prefix: prefix)['Contents']
16
16
 
17
17
  if ret.nil?
18
- puts ' Not found: Direction not exist!'
19
18
  false
20
19
  elsif ret.size == 1
21
20
  service.delete_container(key)
22
21
  true
23
22
  else
24
- raise Fog::Storage::Aliyun::Error, ' Forbidden: Direction not empty!'
23
+ raise Fog::Aliyun::Storage::Error, ' Forbidden: Direction not empty!'
25
24
  false
26
25
  end
27
26
  end
28
27
 
29
28
  def files
30
29
  @files ||= begin
31
- Fog::Storage::Aliyun::Files.new(
30
+ Fog::Aliyun::Storage::Files.new(
32
31
  directory: self,
33
32
  service: service
34
33
  )
@@ -41,7 +40,17 @@ module Fog
41
40
 
42
41
  def save
43
42
  requires :key
44
- service.put_container(key)
43
+
44
+ # Checking whether the key is a bucket and meet the multi-bucket scenario.
45
+ # If the key is a existing bucket, return it directly.
46
+ key = key.chomp('/')
47
+ if !key.nil? && key != '' && key != '.' && !(key.include? '/')
48
+ data = service.get_bucket(key)
49
+ if data.class == Hash && data.key?('Code') && !data['Code'].nil? && !data['Code'].empty?
50
+ service.put_container(key)
51
+ end
52
+ end
53
+
45
54
  true
46
55
  end
47
56
  end
@@ -3,10 +3,10 @@
3
3
  require 'fog/core/model'
4
4
 
5
5
  module Fog
6
- module Storage
7
- class Aliyun
6
+ module Aliyun
7
+ class Storage
8
8
  class File < Fog::Model
9
- identity :key, aliases: 'name'
9
+ identity :key, aliases: ['Key', 'Name', 'name']
10
10
  attribute :date, aliases: 'Date'
11
11
  attribute :content_length, aliases: 'Content-Length', type: :integer
12
12
  attribute :content_type, aliases: 'Content-Type'
@@ -35,29 +35,32 @@ module Fog
35
35
 
36
36
  def copy(target_directory_key, target_file_key, options = {})
37
37
  requires :directory, :key
38
- source_object = if directory.key == ''
38
+ source_bucket, directory_key = collection.check_directory_key(directory.key)
39
+ source_object = if directory_key == ''
39
40
  key
40
41
  else
41
- directory.key + '/' + key
42
+ directory_key + '/' + key
42
43
  end
44
+ target_bucket, target_directory_key = collection.check_directory_key(target_directory_key)
43
45
  target_object = if target_directory_key == ''
44
46
  target_file_key
45
47
  else
46
48
  target_directory_key + '/' + target_file_key
47
49
  end
48
- service.copy_object(nil, source_object, nil, target_object, options)
50
+ service.copy_object(source_bucket, source_object, target_bucket, target_object, options)
49
51
  target_directory = service.directories.new(key: target_directory_key)
50
52
  target_directory.files.get(target_file_key)
51
53
  end
52
54
 
53
55
  def destroy
54
56
  requires :directory, :key
55
- object = if directory.key == ''
57
+ bucket_name, directory_key = collection.check_directory_key(directory.key)
58
+ object = if directory_key == ''
56
59
  key
57
60
  else
58
- directory.key + '/' + key
61
+ directory_key + '/' + key
59
62
  end
60
- service.delete_object(object)
63
+ service.delete_object(object, bucket: bucket_name)
61
64
  true
62
65
  end
63
66
 
@@ -91,12 +94,13 @@ module Fog
91
94
  expires = expires.nil? ? 0 : expires.to_i
92
95
 
93
96
  requires :directory, :key
94
- object = if directory.key == ''
97
+ bucket_name, directory_key = collection.check_directory_key(directory.key)
98
+ object = if directory_key == ''
95
99
  key
96
100
  else
97
- directory.key + '/' + key
101
+ directory_key + '/' + key
98
102
  end
99
- service.get_object_http_url_public(object, expires, options)
103
+ service.get_object_http_url_public(object, expires, options.merge(bucket: bucket_name))
100
104
  end
101
105
 
102
106
  def public_url
@@ -109,18 +113,18 @@ module Fog
109
113
  options['Content-Type'] = content_type if content_type
110
114
  options['Content-Disposition'] = content_disposition if content_disposition
111
115
  options.merge!(metadata_to_headers)
112
-
113
- object = if directory.key == ''
116
+ bucket_name, directory_key = collection.check_directory_key(directory.key)
117
+ object = if directory_key == ''
114
118
  key
115
119
  else
116
- directory.key + '/' + key
120
+ directory_key + '/' + key
117
121
  end
118
122
  if body.is_a?(::File)
119
- data = service.put_object(object, body, options).data
123
+ data = service.put_object(object, body, options.merge(bucket: bucket_name)).data
120
124
  elsif body.is_a?(String)
121
- data = service.put_object_with_body(object, body, options).data
125
+ data = service.put_object_with_body(object, body, options.merge(bucket: bucket_name)).data
122
126
  else
123
- raise Fog::Storage::Aliyun::Error, " Forbidden: Invalid body type: #{body.class}!"
127
+ raise Fog::Aliyun::Storage::Error, " Forbidden: Invalid body type: #{body.class}!"
124
128
  end
125
129
  update_attributes_from(data)
126
130
  refresh_metadata
@@ -168,13 +172,14 @@ module Fog
168
172
 
169
173
  def metadata_attributes
170
174
  if last_modified
171
- object = if directory.key == ''
175
+ bucket_name, directory_key = collection.check_directory_key(directory.key)
176
+ object = if directory_key == ''
172
177
  key
173
178
  else
174
- directory.key + '/' + key
179
+ directory_key + '/' + key
175
180
  end
176
181
 
177
- data = service.head_object(object).data
182
+ data = service.head_object(object, bucket: bucket_name).data
178
183
  if data[:status] == 200
179
184
  headers = data[:headers]
180
185
  headers.select! { |k, _v| metadata_attribute?(k) }
@@ -2,23 +2,67 @@
2
2
 
3
3
  require 'fog/core/collection'
4
4
  require 'fog/aliyun/models/storage/file'
5
+ require 'aliyun/oss'
5
6
 
6
7
  module Fog
7
- module Storage
8
- class Aliyun
8
+ module Aliyun
9
+ class Storage
9
10
  class Files < Fog::Collection
10
11
  attribute :directory
11
12
  attribute :limit
12
- attribute :marker
13
+ attribute :prefix, :aliases => 'Prefix'
13
14
  attribute :path
14
- attribute :prefix
15
-
16
- model Fog::Storage::Aliyun::File
15
+ attribute :common_prefixes, :aliases => 'CommonPrefixes'
16
+ attribute :delimiter, :aliases => 'Delimiter'
17
+ attribute :is_truncated, :aliases => 'IsTruncated'
18
+ attribute :marker, :aliases => 'Marker'
19
+ attribute :max_keys, :aliases => ['MaxKeys', 'max-keys']
20
+
21
+ model Fog::Aliyun::Storage::File
22
+
23
+ # check_directory_key have two functions:
24
+ # 1. trim the directory_key suffix '/'
25
+ # 2. checking whether the directory_key is a bucket.
26
+ # If so, it will return directly to avoid to create a new redundant folder named with directory_key.
27
+ # This point will be applied to multi-bucket and make bucket as a directory scenario.
28
+ def check_directory_key(directory_key)
29
+ bucket_name = nil
30
+ if directory_key.is_a? Array
31
+ directory_key = directory_key[0]
32
+ end
33
+ if directory_key != ''
34
+ # trim the suffix '/'
35
+ directory_key = directory_key.chomp('/')
36
+ # The bucket name can not contain '/', so if directory_key, return directory.
37
+ if directory_key.include? '/'
38
+ directory_key
39
+ else
40
+ data = service.get_bucket(directory_key)
41
+ if data.class == Hash && data.key?('Code') && !data['Code'].nil? && !data['Code'].empty?
42
+ directory_key
43
+ else
44
+ bucket_name = directory_key
45
+ directory_key = ''
46
+ end
47
+ end
48
+ end
49
+ return bucket_name, directory_key
50
+ end
17
51
 
18
- def all(_options = {})
52
+ def all(options = {})
19
53
  requires :directory
20
- prefix = directory.key + '/' if directory.key != '' && directory.key != '.' && !directory.key.nil?
21
- files = service.list_objects(prefix: prefix)['Contents']
54
+ bucket_name, directory_key = check_directory_key(directory.key)
55
+ remap_attributes(options, {
56
+ :delimiter => 'delimiter',
57
+ :marker => 'marker',
58
+ :max_keys => 'max-keys',
59
+ :prefix => 'prefix'
60
+ })
61
+ prefix_value = options['prefix']
62
+ prefix_value = directory_key + '/' + prefix if directory_key != '' && directory_key != '.' && !directory_key.nil?
63
+ options['prefix'] = prefix_value
64
+ options['bucket'] = bucket_name
65
+ files = service.list_objects(options)['Contents']
22
66
  return if files.nil?
23
67
  data = []
24
68
  i = 0
@@ -59,112 +103,88 @@ module Fog
59
103
 
60
104
  def get(key)
61
105
  requires :directory
62
- object = if directory.key == ''
106
+ bucket_name, directory_key = check_directory_key(directory.key)
107
+ object = if directory_key == ''
63
108
  key
64
109
  else
65
- directory.key + '/' + key
110
+ directory_key + '/' + key
66
111
  end
67
112
  begin
68
- data = service.get_object(object)
69
- rescue StandardError => error
70
- case error.response.body
71
- when %r{<Code>NoSuchKey</Code>},%r{<Code>SymlinkTargetNotExist</Code>}
113
+ data = service.get_object(object, nil, bucket: bucket_name)
114
+ lastModified = data['headers'][:last_modified]
115
+ last_modified = (Time.parse(lastModified).localtime if !lastModified.nil? && lastModified != '')
116
+
117
+ date = data['headers'][:date]
118
+ date = (Time.parse(date).localtime if !date.nil? && date != '')
119
+ file_data = {
120
+ body: data[:body],
121
+ content_length: data['headers'][:content_length].to_i,
122
+ key: key,
123
+ last_modified: last_modified,
124
+ content_type: data['headers'][:content_type],
125
+ etag: data['headers'][:etag],
126
+ date: date,
127
+ connection: data['headers'][:connection],
128
+ accept_ranges: data['headers'][:accept_ranges],
129
+ server: data['headers'][:server],
130
+ object_type: data['headers'][:x_oss_object_type]
131
+ }
132
+
133
+ new(file_data)
134
+ rescue AliyunOssSdk::ServerError => error
135
+ case error.error_code
136
+ when %r{NoSuchKey},%r{SymlinkTargetNotExist}
72
137
  nil
73
138
  else
74
139
  raise(error)
75
140
  end
76
141
  end
77
-
78
- contentLen = data[:headers]['Content-Length'].to_i
79
-
80
- if block_given?
81
- pagesNum = (contentLen + Excon::CHUNK_SIZE - 1) / Excon::CHUNK_SIZE
82
-
83
- for i in 1..pagesNum
84
- _start = (i - 1) * Excon::CHUNK_SIZE
85
- _end = i * Excon::CHUNK_SIZE - 1
86
- range = "#{_start}-#{_end}"
87
- begin
88
- data = service.get_object(object, range)
89
- chunk = data[:body]
90
- rescue StandardError => error
91
- case error.response.body
92
- when %r{<Code>NoSuchKey</Code>},%r{<Code>SymlinkTargetNotExist</Code>}
93
- chunk = ''
94
- else
95
- raise(error)
96
- end
97
- end
98
- yield(chunk)
99
- body = nil
100
- end
101
- else
102
- body = data[:body]
103
- end
104
-
105
- lastModified = data[:headers]['Last-Modified']
106
- last_modified = (Time.parse(lastModified).localtime if !lastModified.nil? && lastModified != '')
107
-
108
- date = data[:headers]['Date']
109
- date = (Time.parse(date).localtime if !date.nil? && date != '')
110
- file_data = {
111
- body: body,
112
- content_length: contentLen,
113
- key: key,
114
- last_modified: last_modified,
115
- content_type: data[:headers]['Content-Type'],
116
- etag: data[:headers]['ETag'],
117
- date: date,
118
- connection: data[:headers]['Connection'],
119
- accept_ranges: data[:headers]['Accept-Ranges'],
120
- server: data[:headers]['Server'],
121
- object_type: data[:headers]['x-oss-object-type'],
122
- content_disposition: data[:headers]['Content-Disposition']
123
- }
124
-
125
- new(file_data)
126
142
  end
127
143
 
128
144
  def get_url(key)
129
145
  requires :directory
130
- object = if directory.key == ''
146
+ bucket_name, directory_key = check_directory_key(directory.key)
147
+ object = if directory_key == ''
131
148
  key
132
149
  else
133
- directory.key + '/' + key
150
+ directory_key + '/' + key
134
151
  end
135
- service.get_object_http_url_public(object, 3600)
152
+ service.get_object_http_url_public(object, 3600, bucket: bucket_name)
136
153
  end
137
154
 
138
155
  def get_http_url(key, expires, options = {})
139
156
  requires :directory
140
- object = if directory.key == ''
157
+ bucket_name, directory_key = check_directory_key(directory.key)
158
+ object = if directory_key == ''
141
159
  key
142
160
  else
143
- directory.key + '/' + key
161
+ directory_key + '/' + key
144
162
  end
145
163
  expires = expires.nil? ? 0 : expires.to_i
146
- service.get_object_http_url_public(object, expires, options)
164
+ service.get_object_http_url_public(object, expires, options.merge(bucket: bucket_name))
147
165
  end
148
166
 
149
167
  def get_https_url(key, expires, options = {})
150
168
  requires :directory
151
- object = if directory.key == ''
169
+ bucket_name, directory_key = check_directory_key(directory.key)
170
+ object = if directory_key == ''
152
171
  key
153
172
  else
154
- directory.key + '/' + key
173
+ directory_key + '/' + key
155
174
  end
156
175
  expires = expires.nil? ? 0 : expires.to_i
157
- service.get_object_https_url_public(object, expires, options)
176
+ service.get_object_https_url_public(object, expires, options.merge(bucket: bucket_name))
158
177
  end
159
178
 
160
179
  def head(key, _options = {})
161
180
  requires :directory
162
- object = if directory.key == ''
181
+ bucket_name, directory_key = check_directory_key(directory.key)
182
+ object = if directory_key == ''
163
183
  key
164
184
  else
165
- directory.key + '/' + key
185
+ directory_key + '/' + key
166
186
  end
167
- data = service.head_object(object).data
187
+ data = service.head_object(object, bucket: bucket_name).data
168
188
  return nil if data[:status] == 404
169
189
  lastModified = data[:headers]['Last-Modified']
170
190
  last_modified = (Time.parse(lastModified).localtime if !lastModified.nil? && lastModified != '')
@@ -185,12 +205,19 @@ module Fog
185
205
  object_type: data[:headers]['x-oss-object-type']
186
206
  }
187
207
  new(file_data)
188
- rescue Fog::Storage::Aliyun::NotFound
208
+ rescue Fog::Aliyun::Storage::NotFound
189
209
  nil
190
210
  end
191
211
 
192
212
  def new(attributes = {})
193
213
  requires :directory
214
+ # Sometimes, the v will be a Array, like "Prefix"=>[{}], "Marker"=>[xxxx], "MaxKeys"=>["100"], "IsTruncated"=>["false"]
215
+ # and there needs to parse them
216
+ for k, v in attributes
217
+ if !v.nil? && (v.is_a? Array) && (v.size > 0)
218
+ attributes[k] = v[0]
219
+ end
220
+ end
194
221
  super({ directory: directory }.merge!(attributes))
195
222
  end
196
223
  end