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

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".

Related

Rails download S3 file

I'm uploading files to S3 with paperclip, and now I would like to download them from the same app. So I'm doing what lots of pages says, but if I use 'aws-sdk' it says that AWS::S3::S3Object method 'find' doesn't exist, and If I use 'aws-s3' gem, it says that I need to use 'aws-sdk'.
In controller I'm calling:
aws_object = AWS::S3::S3Object.find #component.folder.path, 'bucket-name'
send_data(aws_object.value, :type => #component.folder_content_type)
EDIT:
My model looks like:
attr_accessible :folder
has_attached_file :folder,
:path => ":rails_root/data/folders/:id/:basename.:extension",
:storage => :s3,
:s3_credentials => {
:bucket => "my-bucket-name",
:access_key_id => "XXXXXXXXX",
:secret_access_key => "XXXXXXXXX"
}
This worked for me:
http://trevorturk.com/2008/12/11/easy-upload-via-url-with-paperclip/
There's a example to download too.
The secret was ".read" :
data = open(asset.uploaded_file.url)
send_data data.read, :type => data.content_type, :x_sendfile => true,:filename => asset.file_name

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"

Amazon S3 path or to_file wont work

Hey guys ive got the following code in my Model
require 'RMagick'
class Upload < ActiveRecord::Base
belongs_to :card
has_attached_file :photo,
:styles => {
:thumb => ["100x100", :jpg],
:pagesize => ["500x400", :jpg],
},
:storage => :s3,
:s3_credentials => "#{RAILS_ROOT}/config/s3.yml",
:path => ":attachment/:id/:style.:extension",
:bucket => 'your_deck',
:default_style => :pagesize
attr_accessor :x1, :y1, :width, :height
def update_attributes(att)
scaled_img = Magick::ImageList.new(self.photo.path)
orig_img = Magick::ImageList.new(self.photo.path(:original))
scale = orig_img.columns.to_f / scaled_img.columns
args = [ att[:x1], att[:y1], att[:width], att[:height] ]
args = args.collect { |a| a.to_i * scale }
orig_img.crop!(*args)
orig_img.write(self.photo.path(:original))
self.photo.reprocess!
self.save
super(att)
end
end
This code works offline however when on Heroku with Amazon S3 it fails to work, ive tried the code with to_file and it also wont work
I get the following error
can't convert Array into String
I had the same problem and it's due to an update to paperclip. I'm surprised heroku are still using this gem version as it will surely affect all their users; I installed a previous version as a plugin and it's fine. Don't forget to remove the gem from your .gems file or specify the previous version in your gems manifest.

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.

How can I set paperclip's storage mechanism based on the current Rails environment?

I have a rails application that has multiple models with paperclip attachments that are all uploaded to S3. This app also has a large test suite that is run quite often. The downside with this is that a ton of files are uploaded to our S3 account on every test run, making the test suite run slowly. It also slows down development a bit, and requires you to have an internet connection in order to work on the code.
Is there a reasonable way to set the paperclip storage mechanism based on the Rails environment? Ideally, our test and development environments would use the local filesystem storage, and the production environment would use S3 storage.
I'd also like to extract this logic into a shared module of some kind, since we have several models that will need this behavior. I'd like to avoid a solution like this inside of every model:
### We don't want to do this in our models...
if Rails.env.production?
has_attached_file :image, :styles => {...},
:path => "images/:uuid_partition/:uuid/:style.:extension",
:storage => :s3,
:url => ':s3_authenticated_url', # generates an expiring url
:s3_credentials => File.join(Rails.root, 'config', 's3.yml'),
:s3_permissions => 'private',
:s3_protocol => 'https'
else
has_attached_file :image, :styles => {...},
:storage => :filesystem
# Default :path and :url should be used for dev/test envs.
end
Update: The sticky part is that the attachment's :path and :url options need to differ depending on which storage system is being used.
Any advice or suggestions would be greatly appreciated! :-)
I like Barry's suggestion better and there's nothing keeping you from setting the variable to a hash, that can then be merged with the paperclip options.
In config/environments/development.rb and test.rb set something like
PAPERCLIP_STORAGE_OPTIONS = {}
And in config/environments/production.rb
PAPERCLIP_STORAGE_OPTIONS = {:storage => :s3,
:s3_credentials => "#{Rails.root}/config/s3.yml",
:path => "/:style/:filename"}
Finally in your paperclip model:
has_attached_file :image, {
:styles => {:thumb => '50x50#', :original => '800x800>'}
}.merge(PAPERCLIP_STORAGE_OPTIONS)
Update: A similar approach was recently implemented in Paperclip for Rails 3.x apps. Environment specific settings can now be set with config.paperclip_defaults = {:storage => :s3, ...}.
You can set global default configuration data in the environment-specific configuration files. For example, in config/environments/production.rb:
Paperclip::Attachment.default_options.merge!({
:storage => :s3,
:bucket => 'wheresmahbucket',
:s3_credentials => {
:access_key_id => ENV['S3_ACCESS_KEY_ID'],
:secret_access_key => ENV['S3_SECRET_ACCESS_KEY']
}
})
After playing around with it for a while, I came up with a module that does what I want.
Inside app/models/shared/attachment_helper.rb:
module Shared
module AttachmentHelper
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def has_attachment(name, options = {})
# generates a string containing the singular model name and the pluralized attachment name.
# Examples: "user_avatars" or "asset_uploads" or "message_previews"
attachment_owner = self.table_name.singularize
attachment_folder = "#{attachment_owner}_#{name.to_s.pluralize}"
# we want to create a path for the upload that looks like:
# message_previews/00/11/22/001122deadbeef/thumbnail.png
attachment_path = "#{attachment_folder}/:uuid_partition/:uuid/:style.:extension"
if Rails.env.production?
options[:path] ||= attachment_path
options[:storage] ||= :s3
options[:url] ||= ':s3_authenticated_url'
options[:s3_credentials] ||= File.join(Rails.root, 'config', 's3.yml')
options[:s3_permissions] ||= 'private'
options[:s3_protocol] ||= 'https'
else
# For local Dev/Test envs, use the default filesystem, but separate the environments
# into different folders, so you can delete test files without breaking dev files.
options[:path] ||= ":rails_root/public/system/attachments/#{Rails.env}/#{attachment_path}"
options[:url] ||= "/system/attachments/#{Rails.env}/#{attachment_path}"
end
# pass things off to paperclip.
has_attached_file name, options
end
end
end
end
(Note: I'm using some custom paperclip interpolations above, like :uuid_partition, :uuid and :s3_authenticated_url. You'll need to modify things as needed for your particular application)
Now, for every model that has paperclip attachments, you just have to include this shared module, and call the has_attachment method (instead of paperclip's has_attached_file)
An example model file: app/models/user.rb:
class User < ActiveRecord::Base
include Shared::AttachmentHelper
has_attachment :avatar, :styles => { :thumbnail => "100x100>" }
end
With this in place, you'll have files saved to the following locations, depending on your environment:
Development:
RAILS_ROOT + public/attachments/development/user_avatars/aa/bb/cc/aabbccddeeff/thumbnail.jpg
Test:
RAILS_ROOT + public/attachments/test/user_avatars/aa/bb/cc/aabbccddeeff/thumbnail.jpg
Production:
https://s3.amazonaws.com/your-bucket-name/user_avatars/aa/bb/cc/aabbccddeeff/thumbnail.jpg
This does exactly what I'm looking for, hopefully it'll prove useful to someone else too. :)
-John
How about this:
Defaults are established in application.rb. The default storage of :filesystem is used, but the configuration for s3 is initialized
Production.rb enables :s3 storage and changes the default path
Application.rb
config.paperclip_defaults =
{
:hash_secret => "LongSecretString",
:s3_protocol => "https",
:s3_credentials => "#{Rails.root}/config/aws_config.yml",
:styles => {
:original => "1024x1024>",
:large => "600x600>",
:medium => "300x300>",
:thumb => "100x100>"
}
}
Development.rb (uncomment this to try with s3 in development mode)
# config.paperclip_defaults.merge!({
# :storage => :s3,
# :bucket => "mydevelopmentbucket",
# :path => ":hash.:extension"
# })
Production.rb:
config.paperclip_defaults.merge!({
:storage => :s3,
:bucket => "myproductionbucket",
:path => ":hash.:extension"
})
In your model:
has_attached_file :avatar
Couldn't you just set an environment variable in production/test/development.rb?
PAPERCLIP_STORAGE_MECHANISM = :s3
Then:
has_attached_file :image, :styles => {...},
:storage => PAPERCLIP_STORAGE_MECHANISM,
# ...etc...
My solution is same with #runesoerensen answer:
I create a module PaperclipStorageOption in config/initializers/paperclip_storage_option.rb
The code is very simple:
module PaperclipStorageOption
module ClassMethods
def options
Rails.env.production? ? production_options : default_options
end
private
def production_options
{
storage: :dropbox,
dropbox_credentials: Rails.root.join("config/dropbox.yml")
}
end
def default_options
{}
end
end
extend ClassMethods
end
and use it in our model
has_attached_file :avatar, { :styles => { :medium => "1200x800>" } }.merge(PaperclipStorageOption.options)
Just it, hope this help
Use the :rails_env interpolation when you define the attachment path:
has_attached_file :attachment, :path => ":rails_root/storage/:rails_env/attachments/:id/:style/:basename.:extension"

Resources