CarrierWave and correct file extension depending on its contents
How can I make CarrierWave add correct extension to filename depend开发者_Go百科ing on its contents? For example, if I upload file "logo" (PNG file without extension) CarrierWave should save it as "logo.png". And file "img.gif" (JPG file with incorrect extension) respectively should be saved as "img.jpg".
There's a couple of things you can do, depending on if you're using process
or version
to do this.
If it's a version, the carrierwave wiki has a way to do conditional versions. https://github.com/jnicklas/carrierwave/wiki/How-to%3A-Do-conditional-processing
version :big, :if => :png? do
process ...
end
protected
def png?(new_file)
new_file.content_type.include? 'png'
end
If you're using the process
method, you might want to take a look at this: https://gist.github.com/995663.
Add these into your code to get around the constraints that process
has
# create a new "process_extensions" method. It is like "process", except
# it takes an array of extensions as the first parameter, and registers
# a trampoline method which checks the extension before invocation
def self.process_extensions(*args)
extensions = args.shift
args.each do |arg|
if arg.is_a?(Hash)
arg.each do |method, args|
processors.push([:process_trampoline, [extensions, method, args]])
end
else
processors.push([:process_trampoline, [extensions, arg, []]])
end
end
end
# our trampoline method which only performs processing if the extension matches
def process_trampoline(extensions, method, args)
extension = File.extname(original_filename).downcase
extension = extension[1..-1] if extension[0,1] == '.'
self.send(method, *args) if extensions.include?(extension)
end
You can then use this to call what used to be process, selectively on each file type
PNG = %w(png)
JPG = %w(jpg jpeg)
GIF = %w(gif)
def extension_white_list
PNG + JPG + GIF
end
process_extensions PNG, :resize_to_fit => [1024, 768]
process_extensions JPG, :...
process_extensions GIF, :...
The problem is in determining the correct content in the first place. Carrierwave uses the MimeType gem which determines its mime-type from the extension. Since, in your case the extension is incorrect you need an alternate way of getting the correct mime-type. This is the best solution I was able to come up with, but it depends on the ability to read the image file using the RMagick gem.
I ran into this same problem and had to override the default set_content_type method for my uploader. This assumes you have Rmagick gem in your Gemfile, so that you can get the correct mime-type from reading the image, as opposed to making a best guess.
Note: This is particularly useful if the image is being used by Prawn which supports only JPG and PNG images.
Uploader Class:
process :set_content_type
def set_content_type #Note we are overriding the default set_content_type_method for this uploader
real_content_type = Magick::Image::read(file.path).first.mime_type
if file.respond_to?(:content_type=)
file.content_type = real_content_type
else
file.instance_variable_set(:@content_type, real_content_type)
end
end
Image Model:
class Image < ActiveRecord::Base
mount_uploader :image, ImageUploader
validates_presence_of :image
validate :validate_content_type_correctly
before_validation :update_image_attributes
private
def update_image_attributes
if image.present? && image_changed?
self.content_type = image.file.content_type
end
end
def validate_content_type_correctly
if not ['image/png', 'image/jpg'].include?(content_type)
errors.add_to_base "Image format is not a valid JPEG or PNG."
return false
else
return true
end
end
end
In your case you can add an additional method that changes the extension based on this correct mime-type (content_type).
精彩评论