diff --git a/lib/aws/s3/object.rb b/lib/aws/s3/object.rb
index bcdf9e1..771267e 100644
--- a/lib/aws/s3/object.rb
+++ b/lib/aws/s3/object.rb
@@ -75,9 +75,6 @@ module S3
# song.content_type = 'application/pdf'
# song.store
#
- # (Keep in mind that due to limitiations in S3's exposed API, the only way to change things like the content_type
- # is to PUT the object onto S3 again. In the case of large files, this will result in fully re-uploading the file.)
- #
# A bevie of information about an object can be had using the about method:
#
# pp song.about
@@ -184,11 +181,22 @@ def copy(key, copy_key, bucket = nil, options = {})
source_key = path!(bucket, key)
default_options = {'x-amz-copy-source' => source_key}
target_key = path!(bucket, copy_key)
- returning put(target_key, default_options) do
+ returning put(target_key, default_options.merge(options)) do
acl(copy_key, bucket, acl(key, bucket)) if options[:copy_acl]
end
end
-
+
+ # Updates the object with key by copying it in-place, preserving the ACL of the existing object.
+ # Useful for updating an object's metadata without having to re-PUT the data.
+ def update(key, bucket = nil, options = {})
+ bucket = bucket_name(bucket)
+ source_key = path!(bucket, key)
+ default_options = {'x-amz-copy-source' => source_key, 'x-amz-metadata-directive' => 'REPLACE'}
+ returning put(source_key, default_options.merge(options)) do
+ acl(key, bucket, acl(key, bucket))
+ end
+ end
+
# Rename the object with key from to have key in to.
def rename(from, to, bucket = nil, options = {})
copy(from, to, bucket, options)
@@ -538,7 +546,12 @@ def store(options = {})
end
alias_method :create, :store
alias_method :save, :store
-
+
+ # Updates the the current object by copying it in place.
+ def update
+ self.class.update(key, bucket.name, about.to_headers)
+ end
+
# Deletes the current object. Trying to save an object after it has been deleted with
# raise a DeletedObject exception.
def delete
@@ -547,8 +560,7 @@ def delete
self.class.delete(key, bucket.name)
end
- # Copies the current object, given it the name copy_name. Keep in mind that due to limitations in
- # S3's API, this operation requires retransmitting the entire object to S3.
+ # Copies the current object, giving it the name copy_name.
def copy(copy_name, options = {})
self.class.copy(key, copy_name, bucket.name, options)
end
diff --git a/test/remote/object_test.rb b/test/remote/object_test.rb
index c63076c..8d83a47 100644
--- a/test/remote/object_test.rb
+++ b/test/remote/object_test.rb
@@ -157,7 +157,7 @@ def test_object
response = fetch_object_at(object.url)
assert (200..299).include?(response.code.to_i)
-
+
# Copy the object
assert_nothing_raised do
@@ -363,6 +363,23 @@ def test_handling_a_path_that_is_not_valid_utf8
end
end
+ def test_updating_an_object_should_replace_its_metadata
+ key = 'updated-object'
+
+ S3Object.store(key, 'value does not matter', TEST_BUCKET)
+ object = S3Object.find(key, TEST_BUCKET)
+
+ object.content_type = 'foo/bar'
+ object.metadata[:foo] = 'bar'
+ object.update
+
+ reloaded_object = S3Object.find(key, TEST_BUCKET)
+ assert_equal 'foo/bar', reloaded_object.content_type
+ assert_equal 'bar', reloaded_object.metadata[:foo]
+ ensure
+ S3Object.delete(key, TEST_BUCKET)
+ end
+
private
def fetch_object_at(url)
Net::HTTP.get_response(URI.parse(url))