Paperclip: PDF thumbnail has wrong content_type on S3 - ruby-on-rails

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.

Related

Rails MIME::Types.type_for(photoshop_1354320001.psd) - MIME Type not found?

I'm using Rails 3, Paperclip(3.3.0), aws-sdk (1.7.1).
My paperclip attachments are being stored securely on S3.
attachment.rb
has_attached_file :attachment,
:storage => :s3,
:s3_credentials => "#{Rails.root}/config/s3.yml",
:s3_protocol => 'https',
:s3_permissions => :private, # Sets the file, not the folder as private in S3
:use_timestamp => false,
:default_style => :original, # NEEDS to be original or download_url method below wont work
:default_url => '/images/:attachment/default_:style.png',
:path => "/:rails_env/private/s/:s_id/uuploaded_files/:basename.:extension"
In order to download the files I generate a secure URL like so:
def authenticated_url(style = nil, expires_in = 1.hour)
mime_type = MIME::Types.type_for(self.attachment_file_name)[0]
attachment.s3_object(style).url_for(:read, :secure => true, :response_content_type => mime_type.to_s, :expires => expires_in).to_s
end
The problem is for PSDs: This is return empty:
Rails MIME::Types.type_for('photoshop_1354320001.psd')
In the code it looks like:
mime_type = MIME::Types.type_for(self.attachment_file_name)[0]
It works for other files fine but not PSDs. Any idea why and how to resolve?
Thanks
Sure. MIME::Types lets you specify custom types.
Stick this into an initializer
# Not quite sure what the appropriate MIMEtype for PSDs are,
# but this is the gist of it.
# .PSB is a larger version of .PSD supporting up to 300000x300000 px
psd_mime_type = MIME::Type.new('image/x-photoshop') do |t|
t.extensions = %w(psd psb)
t.encoding = '8bit'
end
MIME::Types.add psd_mime_type
Now MIME::Types.type_for "test.psd" should give you "image/x-photoshop".

Rails, PaperClip, S3, Heroku: Model icon fields not being saved

I am using Rails 3.2 + Heroku + S3 + Paperclip to store an icon on my User model. The model is not saving the 4 icon fields though. The images are getting processed and saved on S3 correctly and no errors are occurring. I also have another model that has a document being stored via Paperclip and S3. That model works perfectly in all cases. The User icon works locally but not on Heroku.
production.rb relevant configuration
config.paperclip_defaults = {
:storage => :s3,
:s3_credentials => {
:bucket => ENV['AWS_BUCKET'],
:access_key_id => ENV['AWS_ACCESS_KEY_ID'],
:secret_access_key => ENV['AWS_SECRET_ACCESS_KEY']
}
}
User model code:
class User < ActiveRecord::Base
attr_accessible :icon
has_attached_file :icon, :url => "/system/:rails_env/:attachment/:style/:hash.:extension",
:hash_data => ":class/:attachment/:id",
:hash_secret => "superSecretThing",
:styles => { :medium => "300x300>", :thumb => "100x100>" },
:default_url => "/blank.png"
...
Controller code: (This code is kind of crazy because I am AJAXing files Base64 encoded.)
params[:user][:icon_data]
decoded_file = Base64.decode64(data)
begin
split_name = params[:user][:icon_file_name].split(".")
file = Tempfile.new([split_name[0..-2].join("."), ".#{split_name[-1]}"])
file.binmode
file.write(decoded_file)
file.close
#user.icon = open(file)
#user.icon_file_name = params[:user][:icon_file_name]
ensure
file.unlink
end
#user.save
I do an almost identical process on another model with a Paperclip attachment and it works flawlessly. In both cases the attachment is being saved correctly to S3 and no errors are being raised. This gist has example output for a controller action from the Heroku logs.
I am pretty baffled because the other model works fine. The only real difference is that the User attachment does image processing but that part appears to be working fine.
The problem is the same as this one, but the solution there does not apply.
Thoughts?
So the problem is that not including the :path argument makes it try to use the :url parameter for both the url and the path. The real fix is to include the :path parameter in addition to the url.
So for example a fixed configuration that works both locally and on Heroku:
has_attached_file :icon,
:url => "/system/:rails_env/:attachment/:style/:hash.:extension",
:path => "public/system/:rails_env/:attachment/:style/:hash.:extension",
:hash_data => ":class/:attachment/:id",
:hash_secret => "superDuperSecret",
:styles => { :medium => "300x300>", :thumb => "100x100>" },
:default_url => "/blank.png"

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.

RAILS S3 displaying pdf file stored in amazon s3

How to display pdf file which is stored in s3 amazon in rails application???
when uploading files to S3 the filename has to have No Spaces or special caracters.
To upload files with spaces use the following
yourmodel.rb
class Video < ActiveRecord::Base
has_attached_file :video,
:path => ":rails_root/public/system/:attachment/:id/:style/:normalized_video_file_name",
:url => "/system/:attachment/:id/:style/:normalized_video_file_name"
Paperclip.interpolates :normalized_video_file_name do |attachment, style|
attachment.instance.normalized_video_file_name
end
def normalized_video_file_name
"#{self.id}-#{self.video_file_name.gsub( /[^a-zA-Z0-9_\.]/, '_')}"
end
end
What are we doing here? Easy, in has_attached_file we edit the way paperclip returns the path and url by default, the most relevant components when saving and loading the file in order to display it. Paperclip default values are:
path default => ":rails_root/public/system/:attachment/:id/:style/:filename"
url default => "/system/:attachment/:id/:style/:filename"
Values preceded by ’:’ are the standard interpolations paperclip has
http://blog.wyeworks.com/2009/7/13/paperclip-file-rename
you need to add an :s3_headers entry to your has_attachment line:
has_attached_file :asset,
:storage => :s3,
:s3_credentials => "#{RAILS_ROOT}/config/s3.yml",
:path => "uploads/:id/:basename.:extension",
:s3_headers => {"Content-Disposition" => "attachment"},
:s3_permissions => 'authenticated-read',
:s3_protocol => "http",
:bucket => "my_bucket_or_something"

How to pass additional convert options to paperclip on Heroku?

class User < ActiveRecord::Base
has_attached_file :photo, :styles => { :square => "100%", :large => "100%" },
:convert_options => {
:square => "-auto-orient -geometry 70X70#",
:large => "-auto-orient -geometry X300" },
:storage => :s3,
:s3_credentials => "#{RAILS_ROOT}/config/s3.yml",
:path => ":attachment/:id/:style.:extension",
:bucket => 'mybucket'
validates_attachment_size :photo,
:less_than => 5.megabyte
end
Works great on local machine, but gives me an error on Heroku: There was an error processing the thumbnail for stream.20143
The thing is I want to auto-orient photos before resizing, so they resized properly.
The only working variant now(thanks to jonnii) is resizing without auto-orient:
...
as_attached_file :photo, :styles => { :square => "70X70#", :large => "X300" },
:storage => :s3,
:s3_credentials => "#{RAILS_ROOT}/config/s3.yml",
:path => ":attachment/:id/:style.:extension",
:bucket => 'mybucket'
...
How to pass additional convert options to paperclip on Heroku?
UPD
I discover, the trouble in "-auto-orient" option. It seems like this option is broken in version of ImageMagick used by Heroku. I created custom paperclip image processor inherited from paperclip's standard thumbnail:
module Paperclip
class Ao < Thumbnail
def transformation_command
super + " -auto-orient"
end
end
end
It works perfect on local machine, but fails on Heroku.
These are the sizes I use. They all work fine on heroku:
SIZES = {
:original => "640x480>",
:thumb => "150x150#",
:mini => "60x60#",
:micro => "30x30#"
}
Make sure your gem version of paperclip is the same as heroku's. You can specify the specific gem version in your .gems file and in your environment.rb to make sure they line up.
I'm not sure exactly why your convert_options are causing problems, but if I remember correctly paperclip uses ImageScience directly and your chosen options might be incompatible with the read only heroku file system.
If this is critical and you need an answer right now I'd raise a support ticket on heroku. If you get a response make sure you post it back here!

Resources