Paul
Paul

Reputation: 2031

Updating content-type after file upload on Amazon S3 with Amazon-SDK Ruby gem

I'm running a script that updates a metadata field on some of my S3 objects after they have already been uploaded to the S3 bucket. On initialization, I am setting the content-type by checking the file name.

def save_to_amazon(file, s3_object, file_name, meta_path)
  puts "uploaded #{file} to Amazon S3"
  content_type = set_content_type(file_name)
    s3_object.write(file.get_input_stream.read, :metadata => { :folders => meta_path}, :content_type => content_type)
end

At this point, the S3 content-type works fine for these objects. The problem arises when I update the metadata later on. I run something like this:

s3_object.metadata['folders'] = "some string"

At this point, I get an empty string returned when I run s3_objects.content_type after updating the metadata.

s3_object.content_type = is not available.

As far as I can tell from reading the Rdoc there isn't a way to assign content-type after uploading the S3 file. I have tried using the metadata method like

s3.object.metadata['content_type'] = "some string"
s3.object.metadata['content-type'] = "some string"

Both of these appear to assign a new custom metadata attribute instead of updating the object's mime type.

Is there a way to set this, or do I need to completely re-upload the file again?

Upvotes: 4

Views: 13323

Answers (5)

Nuttapon
Nuttapon

Reputation: 101

I'm using a gem "aws-sdk", "~> 2" (2.2.3)

Assume that you have a current file without set content-type (Content-type will be set as a "binary/octet-stream" by default)

How to check a content-type file?

If you use the RestClient as follows:

object mean Aws::S3::Object

bucket = Aws::S3::Bucket.new(bucket_name)
object = bucket.object(key)

RestClient.head(object.presigned_url(:head)) do |resp|
  puts resp.headers
  puts resp.headers[:content_type]
end

How to change a content-type file?

In my case, I wanna change a content-type to 'image/jpeg' which current object is 'binary/octet-stream' so you can

object.copy_from(
  object, 
  content_type: 'image/jpeg', 
  metadata_directive: 'REPLACE'
)

Upvotes: 3

Eric Anderson
Eric Anderson

Reputation: 3792

Although not Ruby I found this project which automatically guessing the mime type based on the extension and resets is via the same copy method that the other answers refers to. It's not terribly quick since it has to copy the blob. If you needed to make it happen faster you could probably divide up the work and copy in parallel via something like IronWorker. I did a similar thing for resetting permissions.

Upvotes: 1

wspruijt
wspruijt

Reputation: 1067

Make sure you set the ACL to :public read, otherwise your files will be unavailable after copying.

This did the trick for me:

bucket.objects.with_prefix('my_assets').each do |obj|
  metadata = obj.head[:metadata]
  content_type = "application/pdf"
  obj.copy_to(obj.key, :metadata => metadata, :content_type => content_type)
  obj.acl = :public_read
end

Upvotes: 2

Paul
Paul

Reputation: 2031

To elaborate on tkotisis reponse, here is what I did to update the content-type using copy_to. You can use s3object.head[:metadata] to pull out the existing metadata to copy it over as referenced here.

amazon_bucket.objects.each do |ob|
    metadata = ob.head[:metadata]
    content_type = "foo/bar"
    ob.copy_to(ob.key, :metadata => metadata, :content_type => content_type)
end

EDIT

amazon_bucket.objects.each do |ob|
    metadata = ob.metadata
    content_type = "foo/bar"
    ob.copy_to(ob.key, :metadata{:foo => metadata[:foo]}, :content_type => content_type)
end

Upvotes: 11

tkotisis
tkotisis

Reputation: 3552

Your example code only modifies your in-memory object. To modify the metadata of the actual S3 object, issue a copy request with destination key the one of your current object.

EDIT

According to the documentation

Using the copy operation, you can rename objects by copying them and deleting the original ones.

When copying an object, you might decide to update some of the metadata values. For example, if your source object is configured to use standard storage, you might choose to use reduced redundancy storage for the object copy. You might also decide to alter some of the user-defined metadata values present on the source object. Note that if you choose to update any of the object's user configurable metadata (system or user-defined) during the copy, then you must explicitly specify all the user configurable metadata, even if you are only changing only one of the metadata values, present on the source object in your request.

I haven't tried it, but using the Ruby SDK this is probably achieved through the

- (S3Object) copy_to(target, options = {})

method.

Upvotes: 7

Related Questions