fog-aliyun 0.3.8 → 0.3.13

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: 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