setting content-type for mp4 files on s3 - ruby-on-rails

I am adding user uploaded videos to my RoRs site with the help of the paperclip gem and s3 storage. For some reason that I can't figure out, whenever a user uploads an mp4 file, s3 sets content-type for that file as application/mp4 instead of video/mp4.
Note that I have registered mp4 mime type in an initializer file:
Mime::Type.lookup_by_extension('mp4').to_s
=> "video/mp4"
Here is the relevant part of my Post model:
has_attached_file :video,
:storage => :s3,
:s3_credentials => "#{Rails.root.to_s}/config/s3.yml",
:path => "/video/:id/:filename"
validates_attachment_content_type :video,
:content_type => ['video/mp4'],
:message => "Sorry, this site currently only supports MP4 video"
What am I missing in my paperclip and/or s3 set-up.
####Update#####
For some reason that is beyond my knowledge of Rails, my default mime types for mp4 contained files is the following:
MIME::Types.type_for("my_video.mp4").to_s
=> "[application/mp4, audio/mp4, video/mp4, video/vnd.objectvideo]"
So, when paperclip send an mp4 file to s3, it seems to identify the file's mime type as the first default, "application/mp4". That is why s3 identifies the file as having a content-type of "application/mp4". Because I want to enable streaming of these mp4 files, I need paperclip to identify the file as having a mime type of "video/mp4".
Is there a way to modify paperclip (maybe in a before_post_process filter) to allow for this, or is there a way to modify rails through an init file to identify mp4 files as being "video/mp4". If I could do either, which way is best.
Thanks for your help

Turns out that I needed to set a default s3 header content_type in the model. This isn't the best solution for me because at some point I might start allowing video containers other than mp4. But it gets me moving on to the next problem.
has_attached_file :video,
:storage => :s3,
:s3_credentials => "#{Rails.root.to_s}/config/s3.yml",
:path => "/video/:id/:filename",
:s3_headers => { "Content-Type" => "video/mp4" }

I did the following:
...
MIN_VIDEO_SIZE = 0.megabytes
MAX_VIDEO_SIZE = 2048.megabytes
VALID_VIDEO_CONTENT_TYPES = ["video/mp4", /\Avideo/] # Note: The regular expression /\Avideo/ will match anything that starts with "video"
has_attached_file :video, {
url: BASE_URL,
path: "video/:id_partition/:filename"
}
validates_attachment :video,
size: { in: MIN_VIDEO_SIZE..MAX_VIDEO_SIZE },
content_type: { content_type: VALID_VIDEO_CONTENT_TYPES }
before_validation :validate_video_content_type, on: :create
before_post_process :validate_video_content_type
def validate_video_content_type
if video_content_type == "application/octet-stream"
# Finds the first match and returns it.
# Alternatively you could use the ".select" method instead which would find all mime types that match any of the VALID_VIDEO_CONTENT_TYPES
mime_type = MIME::Types.type_for(video_file_name).find do |type|
type.to_s.match Regexp.union(VALID_VIDEO_CONTENT_TYPES)
end
self.video_content_type = mime_type.to_s unless mime_type.blank?
end
end
...

Related

Getting URL for public assets in Rails model

I have a Rails application that uses Paperclip and saves images to S3. When the user uploads an asset without an image, it gets the default image set in the Paperclip setup.
My API serves those assets and has the links to the images in the JSON response (using jbuilder), however I can't seem to return the default image URL, it only returns "missing.png" and I wanted it to return the entire URL to the server with the missing image path attached to it.
I'm setting the default url in the model, and I've tried using ActionView::Helpers::AssetUrlHelper to get the image_url but it never works even though it is working inside the rails console. Any idea on what can I do to solve it?
The JBuilder file:
json.profile_picture_smallest asset.profile_picture.url(:smallest)
json.profile_picture_small asset.profile_picture.url(:small)
json.profile_picture_medium asset.profile_picture.url(:medium)
json.profile_picture_large asset.profile_picture.url(:large)
json.profile_picture_original asset.profile_picture.url(:original)
The part of paperclip that is included in the Model
module Picturable
extend ActiveSupport::Concern
included do
has_attached_file :profile_picture, path: '/images/' + name.downcase.pluralize + '/:style/:basename', default_url: "missing.png",
styles: {
smallest: '50x50>',
small: '100x100>',
medium: '200x200>',
large: '400x400>',
png: ['400x400>',:png]
}, :convert_options => {
smallest: '-trim',
small: '-trim',
medium: '-trim',
large: '-trim',
png: '-trim'
}
# Validate the attached image is image/jpg, image/png, etc
validates_attachment_content_type :profile_picture, :content_type => /\Aimage\/.*\Z/
end
def set_uuid_name
begin
self.profile_picture_file_name = SecureRandom.uuid
end while self.class.find_by(:profile_picture_file_name => self.profile_picture_file_name)
end
end
Paperclip config:
Paperclip::Attachment.default_options[:s3_host_name] = 's3hostname'
Development config:
config.paperclip_defaults = {
:storage => :s3,
:s3_credentials => {
:bucket => 'paperclipdev',
:access_key_id => 'accesskey',
:secret_access_key => 'secretaccesskey'
}
}
I think the way to do this is use the asset helpers in your jbuilder file:
json.profile_picture_smallest asset_url(asset.profile_picture.url(:smallest))
It's worth a mention here that you can also pass a symbol method name to paperclip for the default_url parameter if you want the default url to be dynamic based on the model.

What is the content type of json file to AWS S3 with ruby?

I'd like to upload json file to S3 by ruby with paperclip. I coded as following, but it returned the following error.
undefined method `merge' for "application/json":String
Could you tell me how to set content-type of json-file?
product_definition.rb
class ProductDefinition < ActiveRecord::Base
belongs_to :product
has_attached_file :meta_data,
:storage => :s3,
:s3_credentials => "#{Rails.root}/config/s3.yml",
:url => ":s3_domain_url",
:path => "/assets/:id/:style/:basename.:extension",
:s3_host_name => "s3-ap-northeast-1.amazonaws.com"
validates_attachment :meta_data, content_type: 'application/json'
end
products_controller.rb
#product_definition = ProductDefinition.create(meta_data:sample.json)
If you're using a recent version of Paperclip it looks like you formatted the arguments to validates_attachment_content_type incorrectly. I think it should be:
validates_attachment :meta_data, content_type: { content_type: 'application/json' }
See the example in the Paperclip Readme: https://github.com/thoughtbot/paperclip#validations
EDIT: How I came to this conclusion is by noticing the error says it tried to call merge on the string. Merge is meant to be called on a Hash, so therefore it expects a Hash value for :content_type.
What you have is exactly what the current docs on github state, but it does not work:
https://github.com/thoughtbot/paperclip
Other examples use a different way of formatting matching types - so very confusing.
What does seem to work, is a syntax-change which appears to date back years, which is:
validates_attachment :meta_data, attachment_content_type: {content_type: 'application/json'}
You can also put a set of acceptable content-types in an array, such as
validates_attachment :meta_data, attachment_content_type: {content_type: ['application/json', 'application/doc']}

Paperclip says content type is wrong when using an external URL as attachment

I'm using Paperclip 4.2 + Rails 4.1.6 with the following model:
class Post < ActiveRecord::Base
has_attached_file :featured_image, styles: { :medium => "300x300>", :thumb => "100x100>" }
validates_attachment :featured_image, :content_type => { :content_type => ["image/jpeg", "image/gif", "image/png"] }
def featured_image_from_url(url)
self.featured_image = URI.parse(url)
end
end
When I upload a file with the file uploader in my form, everything works fine. The attachment is set and the thumbnails are generated.
However, if I try to use a remote URL pointing to a jpeg image, like specified here, the Post cannot be saved because the attachment has the wrong content type: featured_image_content_type: "binary/octet-stream"
If I force the content type by setting it manually:
post.featured_image_content_type = "image/jpeg"
post.save
then the model is saved successfully.
Hi I don't know whether you found a workaround yet. I have used the following code (amended for your example) to stop Paperclip (4.2.1) raising an exception when a webserver returns an incorrect content-type:
def featured_image_from_url(url)
self.featured_image = URI.parse(url)
# deal with the case where the webserver
# returns an incorrect content-type
adapter = Paperclip.io_adapters.for(featured_image)
self.featured_image_content_type = Paperclip::ContentTypeDetector.new(adapter.path).detect
end
There may be a neater or more correct way!
here is a gem that will help you to download a url to Tempfile so this way you get around the issue with s3 sending stream mime type https://github.com/equivalent/pull_tempfile

How to fix content-type for Aurigma uploads to S3?

Our users have two ways of uploading images. One is through a simple HTML form and the other is through an iPhone app called Aurigma. We use Paperclip to process the images and store them on S3. Images that are uploaded with Aurigma end up having the wrong content-type, which causes them to open as an application.
I tried two solutions:
before_save :set_content_type
def set_content_type
self.image.instance_write(:content_type,"image/png")
end
And:
before_post_process :set_content_type
def set_content_type
self.image.instance_write(:content_type, MIME::Types.type_for(self.image_file_name).to_s)
end
It seems as if both solutions are ignored.
Using paperclip version 3.0.2, Aurigma version 1.3 and I'm uploading a screenshot from my iPhone. This is my paperclip configuration:
has_attached_file :image, {
:convert_options => { :all => '-auto-orient' },
:styles => {
:iphone3 => "150x150",
:web => "300x300"
},
:storage => :s3,
:bucket => ENV['S3_BUCKET'],
:s3_credentials => {
:access_key_id => ENV['S3_KEY'],
:secret_access_key => ENV['S3_SECRET']
},
:path => "/pictures/:id/:style.:extension",
:url => "/pictures/:id/:style.:extension"}
}
I just answered a similar question.
You need to do a copy to itself or use a pre-signed url with the content-type specified in the querystring.
Using the AWS SDK for Ruby and url_for:
object = bucket.objects.myobject
url = object.url_for(:read, :response_content_type => "image/png")
As far as I understand first you upload all the files from client devices to your own server (through Aurigma Up) and then these files are uploaded to Amazon S3. I have similar problem trying to change content-type on client device. This is not possible. You should send files on your server and then change content type before uploading files to S3.

Paperclip: PDF thumbnail has wrong content_type on S3

I'm using Paperclip 2.3.5 within a Rails app to store PDF documents on Amazon S3. For every PDF a JPG thumbnail is generated by ImageMagick. Im' using this configuration in the model:
has_attached_file :file,
:styles => { :thumb => { :geometry => "200x200>",
:format => :jpg
} },
:whiny => false,
:storage => :s3,
:s3_credentials => "#{RAILS_ROOT}/config/s3.yml",
:s3_permissions => 'authenticated-read',
:s3_headers => { 'Expires' => 1.year.from_now.httpdate },
:url => "s3.amazonaws.com",
:path => "documents/:id/:style/:basename.:extension",
:bucket => 'mybucket'
But there is problem: The generated thumbnail is uploaded to S3 with the content_type "application/pdf", which is WRONG, because it's a JPG (you can see the content_type of a file on S3 with a S3 exploring tool like Cyberduck). For the original PDF file this content_type is correct, but not for the thumbnail. This causes trouble in some browsers (e.g. Chrome or Safari) which don't show the thumbnail inline.
Beware: The content_type stored in my database (field "file_content_type") is "application/pdf", which is still correct, because it's the content_type for the original file.
How can I override the content_type for a thumbnail if it should be different from the original file?
This is how we fixed it on brighterplanet.com/research, which has pdf documents and png previews:
has_attached :pdf_document,
:storage => :s3,
# [... other settings ...]
# PDFs work better in Windows 7 / IE if you give them content-type: attachment
:s3_headers => { 'Content-Disposition' => 'attachment' },
:styles => { :preview => { :geometry => '135', :format => :png } }
after_save :fix_thumbnail
def fix_thumbnail(force = false)
# application/pdf and application/x-pdf have both been seen...
return unless force or pdf_document_content_type.include?('pdf')
# set content type and disposition
s3 = AWS::S3.new(YAML.load(File.read("#{RAILS_ROOT}/config/aws_s3.yml")))
t = s3.buckets[PAPERCLIP_BUCKET].objects[pdf_document.path(:thumbnail)]
content = t.read
t.write(:data => content, :content_type => 'image/png', :content_disposition => 'inline', :acl => :public_read)
nil
end
I had to overcome this, not the most elegant solution but I forked Paperclip and hold the patch in my own git repo - https://github.com/svetzal/paperclip
It is a direct replacement for Paperclip, just put in your environment.rb
gem 'twm_paperclip', :lib => 'paperclip'
This is fixed in paperclip >= 2.7, as you can see here:
https://github.com/thoughtbot/paperclip/blob/v2.7/lib/paperclip/storage/s3.rb#L290
the mime-type of the file that is written to S3 is determined specifically before uploading.

Resources