Carrierwave not utilizing default_url when version not present - ruby-on-rails

Essentially, the photo_url(:thumb) displays the intended thumb url even though the file doesn't physically exist.
# encoding: utf-8
class WinePhotoUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage :file
# So the :thumb will be stored in "public/assets/wines/thumb/2008-meritage-750mL.jpg" for example.
def store_dir
"assets/wines/#{version_name}"
end
def cache_dir
Rails.root.join 'tmp/uploads'
end
CarrierWave::SanitizedFile.sanitize_regexp = /[^[:word:]\.\-\+]/
# If no version name is present (original), just provide the thumb path
# This method is never called!!! Why not?
def default_url
ActionController::Base.helpers.asset_path("pages/wines/#{version_name || :thumb}/default.png")
end
# To save disk space, make sure the original image is no larger than the croppable + a few pixels
process :resize_to_fit => [2000, 2000]
version :croppable, :if => :not_cropping? do
process :common
process :resize_and_pad => [1200, 1200, :white]
process :convert => "jpg"
# Override the default naming convention...I store in folders,
# so I don't want the version prepended to the file name.
def full_filename(for_file = model.photo.file)
the_filename
end
end
version :show, :if => :not_cropping? do
process :resize_to_show
process :convert => "jpg"
def full_filename(for_file = model.photo.file)
the_filename
end
end
version :thumb, :from_version => :croppable, :if => :viewing_or_cropping? do
process :custom_thumbnail => [200, 200]
def full_filename(for_file = model.photo.file)
the_filename
end
end
def common(q=100, s='1.2x1+0.75+0.05')
manipulate! do |img|
img.combine_options do |i|
i.quality q
i.antialias
i.background :white
i.flatten
i.density 72
i.profile "#{Rails.root}/lib/color_profiles/sRGB_v4_ICC_preference_displayclass.icc"
i.strip
end
img = yield(img) if block_given?
img.combine_options do |i|
i.unsharp s
end
img
end
end
def resize_to_show
common do |img|
img.combine_options do |i|
i.trim
i.thumbnail 110
i.background :white
i.gravity :center
i.extent 170
end
img
end
end
def custom_thumbnail(width, height)
manipulate! do |img|
img.combine_options do |i|
i.distort :srt, "#{model.crop_x},#{model.crop_y} 1 -30 600,600"
i.repage.+
i.gravity :center
i.crop "#{model.crop_w}x#{model.crop_h}+0+0"
i.repage.+
i.thumbnail "#{width}x#{height}"
i.unsharp '1.2x1+0.75+0.05'
end
img
end
end
def not_cropping? picture
!model.cropping?
end
# For some stupid reason, if I only check for 'model.cropping?' and
# it returns false, the photo_url(:thumb) will always show the default_url
# and never show the actual url! This is terrible logic. The version declarations
# above should only affect the generation of thumbnails, not whether the call
# to photo_url(:thumb) works.
def viewing_or_cropping? picture
model.cropping? or model.changes.merge(model.previous_changes).empty?
end
# Add a white list of extensions which are allowed to be uploaded.
# For images you might use something like this:
def extension_white_list
%w(jpg jpeg gif png tif tiff)
end
# Only modify the version names, not the original uploaded file name
def filename
"#{ model.get_permalink(:normalized => true) }#{ File.extname original_filename }" if original_filename
end
# The filename should be the wine's permalink name (ex., 2008-merlot-reserve-750mL.jpg)
def the_filename
"#{ model.get_permalink(:normalized => true) }.jpg"
end
# Remove this when https://github.com/carrierwaveuploader/carrierwave/issues/1164 is solved.
def recreate_versions!(*versions)
if versions.any?
from_versions = versions.group_by { |v| self.class.versions[v][:options][:from_version] }
from_versions.each do |from, versions|
next if from.nil?
file = CarrierWave::SanitizedFile.new(self.versions[from.to_sym].file)
store_versions!(file, versions)
end
else
super(*versions)
end
end
end
So a call to #wine.photo_url(:thumb) produces
http://dev.mydomain.com/assets/wines/thumb/2006-cabernet-sauvignon-750mL.jpg
when it should be producing
http://dev.mydomain.com/assets/pages/wines/thumb/default.png
because the file doesn't exist.

My best guess is that Carrierwave generates the file names based on the existence of the model's attribute in the database and not whether or not the specific version of the file exists. Not what I was expecting from default_url but it still makes sense in its limited way.

Related

CarrierWave version path

I have model Catagory
class Category < ActiveRecord::Base
mount_uploader :image, CategoryImageUploader
end
I need to save the image with the name (id saved category)
class CategoryImageUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick
storage :file
mounted:
def store_dir
#"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
def store_path(for_file = filename)
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}/#{for_file}"
end
version :small do
process resize_to_fit: [48, 48]
def filename(uploaded_file = file)
if uploaded_file.present?
"small_#{model.id}.#{uploaded_file.extension}"
end
end
end
version :big do
process resize_to_fit: [200, 200]
def filename(uploaded_file = file)
if uploaded_file.present?
"big_#{model.id}.#{uploaded_file.extension}"
end
end
end
def filename(uploaded_file = file)
if uploaded_file.present?
"#{model.id}.#{uploaded_file.extension}"
end
end
# Override the filename of the uploaded files:
# Avoid using model.id or version_name here, see uploader/store.rb for details.
# def filename
# "something.jpg" if original_filename
# end
end
I'm use https://coderwall.com/p/mulldw/custom-file-names-with-carrierwave-and-amazon-s3 to save image like this 3.png 4.png
version :small and version :big aslo save (like big_3.png small_3.png )
but
**Category.last.image_url => "/uploads/category/image/10/10.png"
Category.last.image_url(:small) = > "/uploads/category/image/10/10.png"
Category.last.image_url(:big) = > "/uploads/category/image/10/10.png"**
All links lead to one image 10.png
I need
**Category.last.image_url => "/uploads/category/image/10/10.png"
Category.last.image_url(:small) = > "/uploads/category/image/10/small_10.png"
Category.last.image_url(:big) = > "/uploads/category/image/10/big_10.png"**

Rails 3 + Carrierwave: File Failed to manipulate with MiniMagick, maybe it is not an image? Original Error: MiniMagick::Invalid

Hi I'm trying to set up carrierwave for my Rails project. I've looked at other answers but nothing seems to work. When I try to create an image it gives me this:
1.9.3p392 :003 > bi = BookCoverImage.new
=> #<BookCoverImage id: nil, file: nil, created_at: nil, updated_at: nil, book_id: nil, category_id: nil, collection_id: nil, creator_id: nil, group: "BookCoverImage", homepage_slide_id: nil>
1.9.3p392 :004 > bi.file = File.open("#{Rails.root}/spec/support/sample_img.jpg")
=> #<File:/Users/emai/Documents/mysite/spec/support/sample_img.jpg>
1.9.3p392 :005 > bi.save
(0.1ms) SAVEPOINT active_record_1
(0.1ms) ROLLBACK TO SAVEPOINT active_record_1
=> false
1.9.3p392 :006 > bi.valid?
=> false
1.9.3p392 :007 > bi.errors.full_messages
=> ["File Failed to manipulate with MiniMagick, maybe it is not an image? Original Error: MiniMagick::Invalid"]
I'm using Ruby 1.9.3, Mac Mavericks, and homebrew. Can anyone help?
BookCoverImage model:
class BookCoverImage < Image
default_scope { where(group: self.name) }
belongs_to :book
mount_uploader :file, BookCoverImageUploader
end
It inherits from Image:
class Image < ActiveRecord::Base
attr_accessible :file
after_initialize :set_group
before_save :set_group
private
def set_group
self.group = self.class.name
end
end
My BookCoverImageUploader:
# encoding: utf-8
class BookCoverImageUploader < CarrierWave::Uploader::Base
# Include RMagick or MiniMagick support:
# include CarrierWave::RMagick
include CarrierWave::MiniMagick
# Choose what kind of storage to use for this uploader:
storage :file
# storage :fog
# Override the directory where uploaded files will be stored.
# This is a sensible default for uploaders that are meant to be mounted:
def store_dir
if Rails.env.test? || Rails.env.cucumber?
"#{Rails.root}/public/test/file_uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
else
"uploads/#{model.class.to_s.underscore}/#{model.id}"
end
end
# Provide a default URL as a default if there hasn't been a file uploaded:
# def default_url
# # For Rails 3.1+ asset pipeline compatibility:
# # ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
#
# "/images/fallback/" + [version_name, "default.png"].compact.join('_')
# end
# Process files as they are uploaded:
# process :scale => [200, 300]
#
# def scale(width, height)
# # do something
# end
# Create different versions of your uploaded files:
version :thumb do
process :resize_to_fit => [80, 10000]
end
version :medium do
process :resize_to_fit => [159, 10000]
end
version :main do
process :resize_to_fit => [238, 10000]
end
# Add a white list of extensions which are allowed to be uploaded.
# For images you might use something like this:
def extension_white_list
%w(jpg jpeg gif png)
end
# Override the filename of the uploaded files:
# Avoid using model.id or version_name here, see uploader/store.rb for details.
# def filename
# "something.jpg" if original_filename
# end
end
The error File Failed to manipulate with MiniMagick, maybe it is not an image? Original Error: MiniMagick::Invalid is thrown if ImageMagick fails to identify the given file as an image.
Try this on the command prompt: (Replace with your filename)
> identify spec/support/facebook.jpg
### If image identified correctly, outputs as below
spec/support/facebook.jpg JPEG 400x267 400x267+0+0 8-bit sRGB 13.4KB 0.000u 0:00.000
> identify spec/support/test.png
### If image is invalid, outputs as below
identify: improper image header `spec/support/test.png' # error/png.c/ReadPNGImage/4003.

set content_type and extension with carrierwave depending on its contents

I hava a uploader for pdf documents. After, uploader have a method to create an thumb image of pdf file.
This is the uploader:
require 'carrierwave/processing/mime_types'
class DocumentUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick
include Sprockets::Helpers::RailsHelper
include Sprockets::Helpers::IsolatedHelper
include CarrierWave::MimeTypes
IMAGE_EXTENSIONS = %w(png)
DOCUMENT_EXTENSIONS = %w(pdf)
storage :fog
def store_dir
"documents/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
def extension_white_list
IMAGE_EXTENSIONS + DOCUMENT_EXTENSIONS
end
# 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
def cover
manipulate! do |frame, index|
frame if index.zero?
end
end
version :thumb do
process :cover
#process :resize_to_limit => [110, 60]
process_extensions IMAGE_EXTENSIONS, :resize_to_fit => [110, 60]
process :convert => 'png'
end
process :set_content_type
process :save_content_type_and_size_in_model
def save_content_type_and_size_in_model
model.content_type = file.content_type if file.content_type
model.file_size = file.size
end
end
However when I see the files (both, pdf and thumb image) inside of amazon s3, the extension for thumbnail is .pdf instead of .png:
How can I change the pdf extension to .png and content_type of thumb image?
This works for me to set the extension:
version :thumb do
process :resize_to_fit => [150, 150]
process :convert => 'png'
def full_filename (for_file = model.source.file)
super.chomp(File.extname(super)) + '.png'
end
end

Carrierwave getting image width and height and storing it in an hstore field

My FileHandler model can allows all types of files
class FileHandler < ActiveRecord::Base
serialize :properties, ActiveRecord::Coders::Hstore
mount_uploader :file_path, FileUploader
//I'm already setting some of the file attributes here
def update_file_attributes
if file_path.present? && file_path_changed?
self.file_name = file_path.file.filename
self.file_type = file_path.file.content_type
self.file_size = file_path.file.size
end
end
//I want to get height and width here
#Hstore
%w[ImageHeight ImageWidth].each do |key|
attr_accessible key
define_method(key) do
properties && properties[key]
end
define_method("#{key}=") do |value|
self.properties = (properties || {}).merge(key => value)
end
end
And my fileUploader class
class FileUploader < CarrierWave::Uploader::Base
# Include RMagick or MiniMagick support:
include CarrierWave::RMagick
version :big, :if => :image? do
process :resize_to_limit => [760, nil]
end
version :thumb_big, :if => :image? do
process :resize_to_limit => [200, 200]
end
version :thumb, :if => :image? do
process :resize_to_limit => [160, 160]
end
version :tiny, :if => :image? do
process :resize_to_limit => [40, 40]
end
protected
def image?(new_file)
new_file.content_type.include? 'image'
end
end
My question is, how do i get the height and width property of the original image and store it in the hstore field?
Any help would be greatly appreciated.
Try this
class FileUploader < CarrierWave::Uploader::Base
# Include RMagick or MiniMagick support:
include CarrierWave::RMagick
process :store_geometry, :if => :image?
#......
#......
#......
def store_geometry
if image?(#file)
img = ::Magick::Image::read(#file.file).first
if model
model.ImageWidth = img.columns
model.ImageHeight = img.rows
end
end
end
end
#Hstore
%w[ImageHeight ImageWidth].each do |key|
attr_accessible key
define_method(key) do
properties && properties[key]
end
define_method("#{key}=") do |value|
self.properties = (properties || {}).merge(key => value)
end
end
Assumptions
I'm assuming there's a reason you have the image method that checks if the file is an image, that must mean you're uploading other file formats as well. Well, i've put it to good use here, it calls process_geometry method only if the file is an image.
Hope it helps.
Well you can get the dimension of Image by using Rmagick as far as I know
All I remember is that you can do is this
img = Magick::Image::read("image_file").first
img.columns => width of image
img.rows => height of image
Perhaps then you can set them in HSTORE
Hope this help
Thanks
As an addition to Tommy's comment. This is what I did to save size of every particular image version not just a size of original image. You have to add "geometry" field to your model so you'll have something like: "phone=146x220&tablet=292x440"
class ImageUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick
storage :file
version :thumb do
process :resize_to_fill => [80, 80]
end
version :phone do
process :resize_to_limit => [290, 220]
process :store_geometry => :phone
process :quality => 75
end
version :tablet do
process :resize_to_limit => [580, 440]
process :store_geometry => :tablet
process :quality => 35
end
process
def extension_white_list
%w(jpg jpeg gif png)
end
def store_geometry(version)
manipulate! do |img|
model.geometry ||= ""
model.geometry << "&" unless model.geometry.empty?
model.geometry << "#{version}=#{img.columns}x#{img.rows}"
img
end
end
end

Carrierwave Reset after Reboot

Both the app and db (mongodb) servers were rebooted last night. All carrierwave mounted uploaders are returning the default images for avatars, even though the files still exist.
I am using fog storage on Rackspace CDN. Each user model contains a field of avatar_filename. I tried running user.avatar.recreate_versions! however that errors out due to nil.
Is there any way to restore my images (they still exist!) and prevent this from happening again? I have searched around but it doesn't look like this is a common prom.
In my user model:
# Avatar
mount_uploader :avatar, AvatarUploader
AvatarUploader:
class AvatarUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick
storage :fog
def default_url
"/assets/users/profile-default_#{version_name}.png"
end
# Large
version :large do
resize_to_limit(600, 600)
end
# Small
version :small do
process :crop
resize_to_fill(140, 140)
end
# Thumbnail
version :thumb, :from_version => :small do
resize_to_fill(35, 35)
end
def extension_white_list
%w(jpg jpeg png)
end
def filename
if #filename_created
#filename_created
elsif original_filename
#name ||= Digest::MD5.hexdigest(File.dirname(current_path))
#filename_created = "a_#{timestamp}_#{#name}.#{file.extension}"
#filename_created
end
end
def timestamp
var = :"##{mounted_as}_timestamp"
model.instance_variable_get(var) or model.instance_variable_set(var, Time.now.to_i)
end
def crop
if model.crop_x.present?
resize_to_limit(600, 600)
manipulate! do |img|
x = model.crop_x.to_i
y = model.crop_y.to_i
w = model.crop_w.to_i
h = model.crop_h.to_i
img.crop!(x, y, w, h)
end
end
end
end
Given that the images are there, you could reupload them as remote files with user.remote_avatar_url = "the url for this avatar"
To avoid this in the future you have to keep in mind how you are processing the file name. That process is reapplied each time you do recreate_versions!. Put this code in your uploader to get around this:
class AvatarUploader < CarrierWave::Uploader::Base
def filename
if original_filename
if model && model.read_attribute(:avatar).present?
model.read_attribute(:avatar)
else
# new filename
end
end
end
end
You can find more information about this in the following wiki article: https://github.com/jnicklas/carrierwave/wiki/How-to%3A-Create-random-and-unique-filenames-for-all-versioned-files

Resources