Carrierwave creating all versions of images - ruby-on-rails

I have carrierwave and minimagick installed and im trying to resize the image if its portrait or if its landscape, ive followed the carrierwave documentation but something is not working and it is creating both portrait and landscape images. Ive played around with it now for some time and im all out of ideas! Any ideas will appreciated.
Here is my code my :uploader.rb
class CoverUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage :file
# storage :fog
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
version :landscape, if: :is_landscape?
version :landscape do
process resize_to_fit: [#land_height, 250]
end
version :portrait, if: :is_portrait?
version :portrait do
process resize_to_fit: [250, #port_width]
end
process :store_dimensions
def extension_whitelist
%w(jpg jpeg png)
end
private
def store_dimensions
if file && model
model.width, model.height = ::MiniMagick::Image.open(file.file)[:dimensions]
end
end
def is_landscape? picture
image = MiniMagick::Image.open(picture.path)
image[:width] > image[:height]
width = image[:width]
aspect = image[:width] / image[:height].to_f
#height = aspect * width
end
def is_portrait? picture
image = MiniMagick::Image.open(picture.path)
image[:width] < image[:height]
height = image[:height]
aspect = image[:width] / image[:height].to_f
#width = height / aspect
end
end
So this is creating 3 images instead of 2.
Thanks in advance!

Just taking a wild guess here: You're overriding version with similar but different blocks which are omitting your conditional. Clean it up.
version :portrait, :if => :is_portrait? do
process resize_to_fit: [250, #port_width]
end
version :landscape, :if => :is_landscape? do
process resize_to_fit: [#land_height, 250]
end

Related

Rails 4 Carrierwave + Minimagick: Failed to manipulate with MiniMagick, maybe it is not an image? Original Error: `mogrify -crop

Been researching around but fail to pinpoint the problem: I am following Railscasts PRO #182 on cropping images using JCrop, Carrierwave and Minimagick. As I come to recreating image versions I am prompted the error:
CarrierWave::ProcessingError (Failed to manipulate with MiniMagick, maybe it is not an image? Original Error: mogrify -crop! 250x250+531+32 /tmp/mini_magick20160108-6544-1ec50pf.png failed with error:
mogrify: unrecognized option -crop!' # error/mogrify.c/MogrifyImageCommand/4197.
):
app/uploaders/image_uploader.rb:48:incrop'
app/models/course.rb:15:in crop_image'
app/controllers/courses_controller.rb:12:inupdate'
Can somebody help me understand what this error is indicating?
Model
class Course < ActiveRecord::Base
attr_accessor :crop_x, :crop_y, :crop_w, :crop_h
after_update :crop_image
mount_uploader :image, ImageUploader
def crop_image
image.recreate_versions! if crop_x.present?
end
end
Controller
class CoursesController < ApplicationController
def update
#course = Course.find(params[:id])
if #course.update_attributes(course_params)
if course_params[:image].present?
render :crop
else
redirect_to #course, notice: 'Successfully updated'
end
end
end
def course_params
params.require(:course).permit(:title, :image, :crop_x, :crop_y, :crop_w, :crop_h)
end
end
ImageUploader
class ImageUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
version :thumb do
process :crop
process :resize_to_fit => [250, 250]
end
def crop
if model.crop_x.present?
resize_to_fit(800, 350)
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!("#{w}x#{h}+#{x}+#{y}")
end
end
end
end
Turns out the option -crop! does not exist in command mogrify. The resolution is simply changing .crop! to .crop
i.e. Within ImageUploader:
img.crop!("#{w}x#{h}+#{x}+#{y}") --> img.crop("#{w}x#{h}+#{x}+#{y}")
Having same issue with strip. Only happens when images are large.
First issue of this was in 2012 and was closed without a great solution.

Carrierwave not utilizing default_url when version not present

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.

Resize images with carrierwave if it exceeds height/width

I have looked around and could not find a solution after I tried to do it on my own. When users upload photos, I want for them to be resized if it exceeds my minimum and max dimensions. However I would like two conditionals. The photos that were taken sideways (East/West) should remain within the dimensions I set, and the same follows for the photos taken the tall way (North/South).
For example is a user uploads a photo that's standing the long way and has dimensions of 3264x1840. The upload should be resized to fit 584x329. If the upload is less than 584x329 then it will not adjust the size.
The other example is if user uploads a photo that was taken the tall way and it had dimensions of 2448 x 3264. The upload should be resized to fit 247x329.
I was trying to use MiniMagick with this as I believe that would be the requirement. If I can only use CarrierWave then that's perfect, but I thought MiniMagick was supposed to be used for resizing photos.
The error I receive is 'undefined method resize' for #<ImageUploader:0x007f8606feb9b8>' and it points to#photo = Photo.new(params[:photo])` from def create in the controller.
BTW dimensions are high because those are normally your phones default sizes when you upload a photo.
image_uploader.rb:
class ImageUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
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
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
process :resize => [584, 329]
def resize_to_limit(width, height)
manipulate! do |img|
img.resize "#{width}x#{height}>"
img = yield(img) if block_given?
img
end
end
# Create different versions of your uploaded files:
version :thumb do
process :resize_to_limit => [200, 200]
end
end
Photos Controller:
def create
#photo = Photo.new(params[:photo])
#photo.user = current_user
if #photo.save
flash[:notice] = "Successfully created photos."
redirect_to :back
else
render :action => 'new'
end
end
def resize(width, height, gravity = 'Center')
manipulate! do |img|
img.combine_options do |cmd|
cmd.resize "#{width}"
if img[:width] < img[:height]
cmd.gravity gravity
cmd.background "rgba(255,255,255,0.0)"
cmd.extent "#{width}x#{height}"
end
end
img = yield(img) if block_given?
img
end
end
Change :resize in the image uploader to process :resize_to_fit => [584, 329]

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