fog-aliyun 0.3.8 → 0.3.13

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 111b9682a34af25b9856551baebc1acaff7a7b41c613d37f65ef47a9d5fd2e96
4
- data.tar.gz: 242585bce56cba8e443a991e44f5b1b7306b61ded43c0b57e8af7a4e48bcd408
3
+ metadata.gz: e3be4a1832619663a88cd9ac70eada6d06c24b9e8b7f11da0242516d2e379acd
4
+ data.tar.gz: 5d9338f0e592f82b9f2565ee8fb5c46077d2b1156e72a808a7c476a17e7da8fe
5
5
  SHA512:
6
- metadata.gz: fc4e02bae46ba2520af281d5321fe517b4030439239d771eddc89a235052627831ab13768e9784100654d0e6ba883bb6b15de0ddd5234cac446ac38d007f9424
7
- data.tar.gz: df23687c6c0103993eb2bc5c276ce9f5c45ec8ff7c9478e80de1b7faff5d6cf32764c520316854fae4d3404425778a7912703f274216fe2a1671842142822380
6
+ metadata.gz: 7aebf30618f7e098f37c4a0a0c9a10bba29d33062bc9789547d92caf0dd220a75c7e303b22a64f1c27eec7385be4c4e18b3542aff1430ba5020dea7e763866c0
7
+ data.tar.gz: dd9e04bac82d1acfd60ef696d43b02f181d810aad04a9ad8bff8e6c9038c9a464fd91ad3dbab2c65b491bb1b929ca6cb4f35b9bd06f4485e65a67fe60baaad03
data/.gitignore CHANGED
@@ -23,3 +23,5 @@ tmp
23
23
  *.a
24
24
  mkmf.log
25
25
  gemfiles/*.lock
26
+ morethan100m
27
+ *.log
data/Gemfile CHANGED
@@ -4,3 +4,4 @@ source 'https://rubygems.org'
4
4
 
5
5
  # Specify your gem's dependencies in fog-aliyun.gemspec
6
6
  gemspec
7
+ gem 'aliyun-sdk', git: 'https://github.com/aliyun/aliyun-oss-ruby-sdk.git', branch: 'master'
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).
@@ -26,6 +26,8 @@ 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'
29
31
 
30
32
  spec.add_dependency 'fog-core'
31
33
  spec.add_dependency 'fog-json'
@@ -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,19 +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
- if data.class == Hash && data.key?('Code') && !data['Code'].nil? && !data['Code'].empty?
42
- dir = key + '/'
43
- ret = service.head_object(dir, options)
44
- new(key: key) if ret.data[:status] == 200
45
- else
46
- 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'])
47
69
  end
70
+ directory
48
71
  end
49
72
  else
50
73
  new(key: '')
51
74
  end
52
- rescue Fog::Storage::Aliyun::NotFound
75
+ rescue Fog::Aliyun::Storage::NotFound
53
76
  nil
54
77
  end
55
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
  )
@@ -48,7 +47,6 @@ module Fog
48
47
  if !key.nil? && key != '' && key != '.' && !(key.include? '/')
49
48
  data = service.get_bucket(key)
50
49
  if data.class == Hash && data.key?('Code') && !data['Code'].nil? && !data['Code'].empty?
51
- puts "[INFO] The key #{key} is not a bucket and create one folder named with it."
52
50
  service.put_container(key)
53
51
  end
54
52
  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,32 +35,32 @@ module Fog
35
35
 
36
36
  def copy(target_directory_key, target_file_key, options = {})
37
37
  requires :directory, :key
38
- directory_key = collection.check_directory_key(directory.key)
38
+ source_bucket, directory_key = collection.check_directory_key(directory.key)
39
39
  source_object = if directory_key == ''
40
40
  key
41
41
  else
42
42
  directory_key + '/' + key
43
43
  end
44
- target_directory_key = collection.check_directory_key(target_directory_key)
44
+ target_bucket, target_directory_key = collection.check_directory_key(target_directory_key)
45
45
  target_object = if target_directory_key == ''
46
46
  target_file_key
47
47
  else
48
48
  target_directory_key + '/' + target_file_key
49
49
  end
50
- service.copy_object(nil, source_object, nil, target_object, options)
50
+ service.copy_object(source_bucket, source_object, target_bucket, target_object, options)
51
51
  target_directory = service.directories.new(key: target_directory_key)
52
52
  target_directory.files.get(target_file_key)
53
53
  end
54
54
 
55
55
  def destroy
56
56
  requires :directory, :key
57
- directory_key = collection.check_directory_key(directory.key)
57
+ bucket_name, directory_key = collection.check_directory_key(directory.key)
58
58
  object = if directory_key == ''
59
59
  key
60
60
  else
61
61
  directory_key + '/' + key
62
62
  end
63
- service.delete_object(object)
63
+ service.delete_object(object, bucket: bucket_name)
64
64
  true
65
65
  end
66
66
 
@@ -94,13 +94,13 @@ module Fog
94
94
  expires = expires.nil? ? 0 : expires.to_i
95
95
 
96
96
  requires :directory, :key
97
- directory_key = collection.check_directory_key(directory.key)
97
+ bucket_name, directory_key = collection.check_directory_key(directory.key)
98
98
  object = if directory_key == ''
99
99
  key
100
100
  else
101
101
  directory_key + '/' + key
102
102
  end
103
- service.get_object_http_url_public(object, expires, options)
103
+ service.get_object_http_url_public(object, expires, options.merge(bucket: bucket_name))
104
104
  end
105
105
 
106
106
  def public_url
@@ -113,18 +113,18 @@ module Fog
113
113
  options['Content-Type'] = content_type if content_type
114
114
  options['Content-Disposition'] = content_disposition if content_disposition
115
115
  options.merge!(metadata_to_headers)
116
- directory_key = collection.check_directory_key(directory.key)
116
+ bucket_name, directory_key = collection.check_directory_key(directory.key)
117
117
  object = if directory_key == ''
118
118
  key
119
119
  else
120
120
  directory_key + '/' + key
121
121
  end
122
122
  if body.is_a?(::File)
123
- data = service.put_object(object, body, options).data
123
+ data = service.put_object(object, body, options.merge(bucket: bucket_name)).data
124
124
  elsif body.is_a?(String)
125
- 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
126
126
  else
127
- raise Fog::Storage::Aliyun::Error, " Forbidden: Invalid body type: #{body.class}!"
127
+ raise Fog::Aliyun::Storage::Error, " Forbidden: Invalid body type: #{body.class}!"
128
128
  end
129
129
  update_attributes_from(data)
130
130
  refresh_metadata
@@ -172,14 +172,14 @@ module Fog
172
172
 
173
173
  def metadata_attributes
174
174
  if last_modified
175
- directory_key = collection.check_directory_key(directory.key)
175
+ bucket_name, directory_key = collection.check_directory_key(directory.key)
176
176
  object = if directory_key == ''
177
177
  key
178
178
  else
179
179
  directory_key + '/' + key
180
180
  end
181
181
 
182
- data = service.head_object(object).data
182
+ data = service.head_object(object, bucket: bucket_name).data
183
183
  if data[:status] == 200
184
184
  headers = data[:headers]
185
185
  headers.select! { |k, _v| metadata_attribute?(k) }
@@ -2,18 +2,23 @@
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
+ 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']
15
20
 
16
- model Fog::Storage::Aliyun::File
21
+ model Fog::Aliyun::Storage::File
17
22
 
18
23
  # check_directory_key have two functions:
19
24
  # 1. trim the directory_key suffix '/'
@@ -21,6 +26,10 @@ module Fog
21
26
  # If so, it will return directly to avoid to create a new redundant folder named with directory_key.
22
27
  # This point will be applied to multi-bucket and make bucket as a directory scenario.
23
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
24
33
  if directory_key != ''
25
34
  # trim the suffix '/'
26
35
  directory_key = directory_key.chomp('/')
@@ -29,25 +38,31 @@ module Fog
29
38
  directory_key
30
39
  else
31
40
  data = service.get_bucket(directory_key)
32
- puts "[DEBUG] Getting the bucket named with directory name #{directory_key}..."
33
41
  if data.class == Hash && data.key?('Code') && !data['Code'].nil? && !data['Code'].empty?
34
- puts "[INFO] The directory name #{directory_key} is not a bucket and will create one folder named with it."
35
42
  directory_key
36
43
  else
37
- puts "[INFO] The directory name #{directory_key} is a bucket and store objects directly."
38
- ''
44
+ bucket_name = directory_key
45
+ directory_key = ''
39
46
  end
40
47
  end
41
- else
42
- ''
43
48
  end
49
+ return bucket_name, directory_key
44
50
  end
45
51
 
46
- def all(_options = {})
52
+ def all(options = {})
47
53
  requires :directory
48
- directory_key = check_directory_key(directory.key)
49
- prefix = directory_key + '/' if directory_key != '' && directory_key != '.' && !directory_key.nil?
50
- 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']
51
66
  return if files.nil?
52
67
  data = []
53
68
  i = 0
@@ -88,117 +103,88 @@ module Fog
88
103
 
89
104
  def get(key)
90
105
  requires :directory
91
- directory_key = check_directory_key(directory.key)
106
+ bucket_name, directory_key = check_directory_key(directory.key)
92
107
  object = if directory_key == ''
93
108
  key
94
109
  else
95
110
  directory_key + '/' + key
96
111
  end
97
112
  begin
98
- data = service.get_object(object)
99
- rescue StandardError => error
100
- case error.response.body
101
- 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}
102
137
  nil
103
138
  else
104
139
  raise(error)
105
140
  end
106
141
  end
107
-
108
- contentLen = data[:headers]['Content-Length'].to_i
109
-
110
- if block_given?
111
- pagesNum = (contentLen + Excon::CHUNK_SIZE - 1) / Excon::CHUNK_SIZE
112
-
113
- for i in 1..pagesNum
114
- _start = (i - 1) * Excon::CHUNK_SIZE
115
- _end = i * Excon::CHUNK_SIZE - 1
116
- range = "#{_start}-#{_end}"
117
- begin
118
- data = service.get_object(object, range)
119
- chunk = data[:body]
120
- rescue StandardError => error
121
- case error.response.body
122
- when %r{<Code>NoSuchKey</Code>},%r{<Code>SymlinkTargetNotExist</Code>}
123
- chunk = ''
124
- else
125
- raise(error)
126
- end
127
- end
128
- yield(chunk)
129
- body = nil
130
- end
131
- else
132
- body = data[:body]
133
- end
134
-
135
- lastModified = data[:headers]['Last-Modified']
136
- last_modified = (Time.parse(lastModified).localtime if !lastModified.nil? && lastModified != '')
137
-
138
- date = data[:headers]['Date']
139
- date = (Time.parse(date).localtime if !date.nil? && date != '')
140
- file_data = {
141
- body: body,
142
- content_length: contentLen,
143
- key: key,
144
- last_modified: last_modified,
145
- content_type: data[:headers]['Content-Type'],
146
- etag: data[:headers]['ETag'],
147
- date: date,
148
- connection: data[:headers]['Connection'],
149
- accept_ranges: data[:headers]['Accept-Ranges'],
150
- server: data[:headers]['Server'],
151
- object_type: data[:headers]['x-oss-object-type'],
152
- content_disposition: data[:headers]['Content-Disposition']
153
- }
154
-
155
- new(file_data)
156
142
  end
157
143
 
158
144
  def get_url(key)
159
145
  requires :directory
160
- directory_key = check_directory_key(directory.key)
146
+ bucket_name, directory_key = check_directory_key(directory.key)
161
147
  object = if directory_key == ''
162
148
  key
163
149
  else
164
150
  directory_key + '/' + key
165
151
  end
166
- service.get_object_http_url_public(object, 3600)
152
+ service.get_object_http_url_public(object, 3600, bucket: bucket_name)
167
153
  end
168
154
 
169
155
  def get_http_url(key, expires, options = {})
170
156
  requires :directory
171
- directory_key = check_directory_key(directory.key)
157
+ bucket_name, directory_key = check_directory_key(directory.key)
172
158
  object = if directory_key == ''
173
159
  key
174
160
  else
175
161
  directory_key + '/' + key
176
162
  end
177
163
  expires = expires.nil? ? 0 : expires.to_i
178
- service.get_object_http_url_public(object, expires, options)
164
+ service.get_object_http_url_public(object, expires, options.merge(bucket: bucket_name))
179
165
  end
180
166
 
181
167
  def get_https_url(key, expires, options = {})
182
168
  requires :directory
183
- directory_key = check_directory_key(directory.key)
169
+ bucket_name, directory_key = check_directory_key(directory.key)
184
170
  object = if directory_key == ''
185
171
  key
186
172
  else
187
173
  directory_key + '/' + key
188
174
  end
189
175
  expires = expires.nil? ? 0 : expires.to_i
190
- service.get_object_https_url_public(object, expires, options)
176
+ service.get_object_https_url_public(object, expires, options.merge(bucket: bucket_name))
191
177
  end
192
178
 
193
179
  def head(key, _options = {})
194
180
  requires :directory
195
- directory_key = check_directory_key(directory.key)
181
+ bucket_name, directory_key = check_directory_key(directory.key)
196
182
  object = if directory_key == ''
197
183
  key
198
184
  else
199
185
  directory_key + '/' + key
200
186
  end
201
- data = service.head_object(object).data
187
+ data = service.head_object(object, bucket: bucket_name).data
202
188
  return nil if data[:status] == 404
203
189
  lastModified = data[:headers]['Last-Modified']
204
190
  last_modified = (Time.parse(lastModified).localtime if !lastModified.nil? && lastModified != '')
@@ -219,12 +205,19 @@ module Fog
219
205
  object_type: data[:headers]['x-oss-object-type']
220
206
  }
221
207
  new(file_data)
222
- rescue Fog::Storage::Aliyun::NotFound
208
+ rescue Fog::Aliyun::Storage::NotFound
223
209
  nil
224
210
  end
225
211
 
226
212
  def new(attributes = {})
227
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
228
221
  super({ directory: directory }.merge!(attributes))
229
222
  end
230
223
  end