开发者

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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜