Class: OpenStack::Swift::StorageObject

Inherits:
Object
  • Object
show all
Defined in:
lib/openstack/swift/storage_object.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(container, objectname, force_exists = false) ⇒ StorageObject

Builds a new OpenStack::Swift::StorageObject in the specified container. If force_exist is set, the object must exist or a OpenStack::Exception::ItemNotFound will be raised. If not, an “empty” StorageObject will be returned, ready for data via the write method

The container parameter must be an OpenStack::Swift::Container object.

This constructor is typically not called directly. You can get a reference to an existing Object via OpenStack::Swift::Container::object method or create a new Object via OpenStack::Swift::Container::create_object method



25
26
27
28
29
30
31
32
33
# File 'lib/openstack/swift/storage_object.rb', line 25

def initialize(container, objectname, force_exists = false)
  @container = container
  @containername = container.name
  @name = objectname

  if force_exists
    raise OpenStack::Exception::ItemNotFound.new("No Object \"#{@name}\" found in Container \"#{@containername}\"", "404", "") unless container.object_exists?(objectname)
  end
end

Instance Attribute Details

#containerObject (readonly)

Returns the value of attribute container.



10
11
12
# File 'lib/openstack/swift/storage_object.rb', line 10

def container
  @container
end

#metadataObject (readonly)

returns just the user defined custom metadata obj.metadata

> ”herpa“=>”derp“



89
90
91
# File 'lib/openstack/swift/storage_object.rb', line 89

def 
  @metadata
end

#nameObject (readonly)

Returns the value of attribute name.



9
10
11
# File 'lib/openstack/swift/storage_object.rb', line 9

def name
  @name
end

Class Method Details

.create(container, objectname, headers = {}, data = nil) ⇒ Object

create a new Object in a given Container optional headers:

 :metadata=>{key=>value, key1=>value1, ...
 :content_type=>content type of created object
 :etag=>MD5 checksum of object data to be compared to that on server side
 :cache_control=>cache control header
 :manifest=>set manifest header for segmented large object
}

The container parameter must be an OpenStack::Swift::Container object. Typically you’d create an Object by first getting a Container: cont = os.container(“foo_container”) cont.create_object(“my_new_object”, {}, “object data”)



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/openstack/swift/storage_object.rb', line 49

def self.create(container, objectname, headers={}, data=nil)
  provided_headers = (headers[:metadata] || {}).inject({}){|res, (k,v)| ((k.to_s.match /^X-Object-Meta-/i) ? res[k.to_s]=v : res["X-Object-Meta-#{k.to_s}"]=v) ;res}
  provided_headers["content-type"] = headers[:content_type] unless headers[:content_type].nil?
  provided_headers["ETag"] = headers[:etag] unless headers[:etag].nil?
  provided_headers["Cache-Control"] = headers[:cache_control] unless headers[:cache_control].nil?
  provided_headers["X-Object-Manifest"] = headers[:manifest] unless headers[:manifest].nil?
  if data.nil? #just create an empty object
    path = "/#{container.name}/#{objectname}"
    provided_headers["content-length"] = "0"
    container.swift.connection.req("PUT", URI.encode(path), {:headers=>provided_headers})
  else
    self.new(container, objectname).write(data, provided_headers)
  end
  self.new(container, objectname)
end

Instance Method Details

#bytesObject

Size of the object (in bytes) obj.bytes

> “493009”



96
97
98
# File 'lib/openstack/swift/storage_object.rb', line 96

def bytes
  self.[:bytes]
end

#cache_controlObject

Cache-Control header of the object obj.cache_control

> “application/json”



124
125
126
# File 'lib/openstack/swift/storage_object.rb', line 124

def cache_control
  self.[:cache_control]
end

#content_typeObject

Content type of the object data obj.content_type

> “application/json”



117
118
119
# File 'lib/openstack/swift/storage_object.rb', line 117

def content_type
  self.[:content_type]
end

#copy(object_name, container_name, headers = {}) ⇒ Object

Copy this object to a new location (optionally in a new container)

You must supply a name for the new object as well as a container name.

new_object = object.copy("new_obj", "my_container")

You may also supply a hash of headers to set Content-Type, or custom key=>value metadata: optional headers:

                :metadata=>{key=>value, key1=>value1, ...
                :content_type=>content type of created object
              }

copied = object.copy('newfile.tmp', "my_container", :metadata=>{:herp=>"derp", "X-Object-Meta-foo"=>"bar } )
=> => #<OpenStack::Swift::StorageObject:0xb728974c  .....

Returns the new OpenStack::Swift::StorageObject for the copied item.



285
286
287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/openstack/swift/storage_object.rb', line 285

def copy(object_name, container_name, headers = {})
  provided_headers = (headers[:metadata] || {}).inject({}){|res, (k,v)| ((k.to_s.match /^X-Object-Meta-/i) ? res[k.to_s]=v : res["X-Object-Meta-#{k.to_s}"]=v) ;res}
  provided_headers["content-type"] = headers[:content_type] unless headers[:content_type].nil?
  provided_headers["X-Copy-From"] = "/#{@containername}/#{@name}"
  provided_headers["content-length"] = "0"
  path = "/#{container_name}/#{object_name}"
  begin
    response = @container.swift.connection.req("PUT", URI.encode(path), {:headers=>provided_headers})
  rescue OpenStack::Exception::ItemNotFound => not_found
    msg = "Can't copy \"#{@name}\": No Object \"#{@name}\" found in Container \"#{@containername}\".  #{not_found.message}"
    raise OpenStack::Exception::ItemNotFound.new(msg, not_found.response_code, not_found.response_body)
  end
  OpenStack::Swift::StorageObject.new(@container.swift.container(container_name), object_name)
end

#data(size = -1,, offset = 0, headers = {}) ⇒ Object Also known as: read

Retrieves the data from an object and stores the data in memory. The data is returned as a string. Throws a OpenStack::Exception::ItemNotFound if the object doesn’t exist.

If the optional size and range arguments are provided, the call will return the number of bytes provided by size, starting from the offset provided in offset.

object.data
=> "This is the text stored in the file"


136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/openstack/swift/storage_object.rb', line 136

def data(size = -1, offset = 0, headers = {})
  headers = {'content-type'=>'application/json'}
  if size.to_i > 0
    range = sprintf("bytes=%d-%d", offset.to_i, (offset.to_i + size.to_i) - 1)
    headers['Range'] = range
  end
  path = "/#{@containername}/#{@name}"
  begin
    response = @container.swift.connection.req("GET", URI.encode(path), {:headers=>headers})
    response.body
  rescue OpenStack::Exception::ItemNotFound => not_found
    msg = "No Object \"#{@name}\" found in Container \"#{@containername}\".  #{not_found.message}"
    raise OpenStack::Exception::ItemNotFound.new(msg, not_found.response_code, not_found.response_body)
  end
end

#data_stream(size = -1,, offset = 0, &block) ⇒ Object

Retrieves the data from an object and returns a stream that must be passed to a block. Throws a OpenStack::Exception::ItemNotFound if the object doesn’t exist.

If the optional size and range arguments are provided, the call will return the number of bytes provided by size, starting from the offset provided in offset.

The method returns the HTTP response object

data = ""
object.data_stream do |chunk| data += chunk end
=> #<Net::HTTPOK 200 OK readbody=true>
data
=> "This is the text stored in the file"


166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/openstack/swift/storage_object.rb', line 166

def data_stream(size = -1, offset = 0, &block)
  headers = {'content-type'=>'application/json'}
  if size.to_i > 0
    range = sprintf("bytes=%d-%d", offset.to_i, (offset.to_i + size.to_i) - 1)
    headers['Range'] = range
  end
  server = @container.swift.connection.service_host
  path = @container.swift.connection.service_path + URI.encode("/#{@containername}/#{@name}")
  port = @container.swift.connection.service_port
  scheme = @container.swift.connection.service_scheme
  response = @container.swift.connection.csreq("GET", server, path, port, scheme, headers, nil, 0, &block)
  raise OpenStack::Exception.raise_exception(response) unless response.code.match(/^20.$/)
  response
end

#etagObject

ETag of the object data obj.etag

> “494e444f92a8082dabac80a74cdf2c3b”



110
111
112
# File 'lib/openstack/swift/storage_object.rb', line 110

def etag
  self.[:etag]
end

#last_modifiedObject

Date of the object’s last modification obj.last_modified

> “Thu, 26 Apr 2012 09:22:51 GMT”



103
104
105
# File 'lib/openstack/swift/storage_object.rb', line 103

def last_modified
  self.[:last_modified]
end

#manifestObject

Returns the object’s manifest.

object.manifest
=> "container/prefix"


212
213
214
# File 'lib/openstack/swift/storage_object.rb', line 212

def manifest
  self.[:manifest]
end

#move(object_name, container_name, headers = {}) ⇒ Object

Takes the same options as the copy method, only it does a copy followed by a delete on the original object.

Returns the new OpenStack::Swift::StorageObject for the moved item. You should not attempt to use the old object after doing a move. optional headers:

  :metadata=>{key=>value, key1=>value1, ...
  :content_type=>content type of created object
}


309
310
311
312
313
# File 'lib/openstack/swift/storage_object.rb', line 309

def move(object_name, container_name, headers={})
  new_object = self.copy(object_name, container_name, headers)
  @container.delete_object(@name)
  new_object
end

#object_metadataObject

Retrieves Metadata for the object

object = container.object("conversion_helper.rb")
=> #<OpenStack::Swift::StorageObject:0xb7692488  ....
object.object_metadata
=> {:manifest=>nil, :bytes=>"1918", :content_type=>"application/octet-stream", :metadata=>{"foo"=>"bar, "herpa"=>"derp"}, :etag=>"1e5b089a1d92052bcf759d86465143f8", :last_modified=>"Tue, 17 Apr 2012 08:46:35 GMT", :cache_control=>nil}


71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/openstack/swift/storage_object.rb', line 71

def 
  path = "/#{@containername}/#{@name}"
  response = @container.swift.connection.req("HEAD", URI.encode(path))
  resphash = response.to_hash
  meta = { :bytes=>resphash["content-length"][0],
           :content_type=>resphash["content-type"][0],
           :last_modified=>resphash["last-modified"][0],
           :etag=>resphash["etag"][0],
           :cache_control=> (resphash.has_key?("cache-control") ? resphash["cache-control"][0] : nil),
           :manifest=> (resphash.has_key?("x-object-manifest") ? resphash["x-object-manifest"][0] : nil),
           :metadata=>{}}
  resphash.inject({}){|res, (k,v)| meta[:metadata].merge!({ k.gsub("x-object-meta-", "") => v.first }) if k.match(/^x-object-meta-/)}
  meta
end

#set_manifest(manifest) ⇒ Object

Sets the manifest for an object. By passing a string as an argument, you can set the manifest for an object. However, setting manifest will overwrite any existing manifest for the object.

Throws OpenStack::Exception::ItemNotFound if the object doesn’t exist. Returns true if the call is successful.



222
223
224
225
226
227
228
229
230
231
232
# File 'lib/openstack/swift/storage_object.rb', line 222

def set_manifest(manifest)
  headers = {'X-Object-Manifest' => manifest}
  path = "/#{@containername}/#{@name}"
  begin
    response = @container.swift.connection.req("POST", URI.encode(path), {:headers=>headers})
  rescue OpenStack::Exception::ItemNotFound => not_found
    msg = "Can't set manifest: No Object \"#{@name}\" found in Container \"#{@containername}\".  #{not_found.message}"
    raise OpenStack::Exception::ItemNotFound.new(msg, not_found.response_code, not_found.response_body)
  end
  true
end

#set_metadata(metadatahash) ⇒ Object Also known as: metadata=

Sets the metadata for an object. By passing a hash as an argument, you can set the metadata for an object. However, setting metadata will overwrite any existing metadata for the object. Returns true if the call was successful. Throws OpenStack::Exception::ItemNotFound if the object doesn’t exist.

The OpenStack mandated ‘X-Object-Meta’ prefix is optional:

obj.set_metadata(“X-Object-Meta-herpa”=>“derp”, “author”=>“me”)

> true

obj.metadata

> “author”=>“me”, “herpa”=>“derp”



193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/openstack/swift/storage_object.rb', line 193

def (metadatahash)
  headers  = metadatahash.inject({}){|res, (k,v)| ((k.to_s.match /^X-Object-Meta-/i) ? res[k.to_s]=v : res["X-Object-Meta-#{k.to_s}"]=v ) ;res}
  headers['content-type'] = 'application/json'
  path = "/#{@containername}/#{@name}"
  begin
    response = @container.swift.connection.req("POST", URI.encode(path), {:headers=>headers})
  rescue OpenStack::Exception::ItemNotFound => not_found
    msg = "Can't set metadata: No Object \"#{@name}\" found in Container \"#{@containername}\".  #{not_found.message}"
    raise OpenStack::Exception::ItemNotFound.new(msg, not_found.response_code, not_found.response_body)
  end
  true
end

#to_sObject

:nodoc:



315
316
317
# File 'lib/openstack/swift/storage_object.rb', line 315

def to_s # :nodoc:
  @name
end

#write(data, headers = {}) ⇒ Object

Takes supplied data and writes it to the object, saving it. You can supply an optional hash of headers, including Content-Type and ETag, that will be applied to the object.

If you would rather stream the data in chunks, instead of reading it all into memory at once, you can pass an IO object for the data, such as: object.write(open(‘/path/to/file.mp3’))

You can compute your own MD5 sum and send it in the “ETag” header. If you provide yours, it will be compared to the MD5 sum on the server side.

Returns true on success, raises exceptions if stuff breaks.

object = container.create_object("newfile.txt")

object.write("This is new data")
=> true

object.data
=> "This is new data"


255
256
257
258
259
260
261
262
263
264
265
# File 'lib/openstack/swift/storage_object.rb', line 255

def write(data, headers = {})
  server = @container.swift.connection.service_host
  path = @container.swift.connection.service_path + URI.encode("/#{@containername}/#{@name}")
  port = @container.swift.connection.service_port
  scheme = @container.swift.connection.service_scheme
  body = (data.is_a?(String))? StringIO.new(data) : data
  body.binmode if (body.respond_to?(:binmode))
  response = @container.swift.connection.put_object(server, path, port, scheme, headers, body, 0)
  raise OpenStack::Exception.raise_exception(response) unless response.code.match(/^20.$/)
  true
end