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]
Related
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
In my Rails app, I enable users to upload images using Carrierwave and Amazon S3. I want to implement a feature that lets users edit existing images by rotating it 90 degrees.
I'm confused about where this code would go. Does it go in the image uploader file, or the image controller? And how is it called? I believe it should look something like this:
image = Image.find(params[:id])
image_obj = MiniMagick::Image.read(image.file)
image_obj.rotate(-90)
image_obj.write(image.file)
But I haven't been able to find examples to help me. If anyone can give me a pointer in the right direction, I would really appreciate it!
Edit
Thanks to deep for their thorough response! Here is what I ended up doing:
In my view:
# image.html.erb:
<%= link_to rotate_image_path(:id => image.id), :remote => true %>
In my controller:
# image_controller.rb:
def rotate
#image = Image.find(params[:id])
#image.rotated = true
#image.save
respond_to do |format|
format.js { render :nothing => true }
end
end
In my model:
# image.rb
attr_accessible :rotated
after_save :rotate_image, if: ->(obj){obj.rotated.present? && obj.rotated?}
def rotate_image
self.image_path.recreate_versions!
end
In my uploader:
# image_uploader.rb
process :rotate_img
def rotate_img
if model.rotated.present? && model.rotated?
manipulate! do |img|
img.rotate '-90'
img
end
end
end
The only real change I made was in the uploader, where I ran into errors trying to do a condition process. I put the conditional within the rotate_img method.
Here's my solution
First define a attribute accessor in your model and on update set it to true.
In your model
#image.rb
attr_accessor :rotate
In your controller
#images_controller.rb
def update
#image = Image.find(params[:id])
#image.rotate = true
#image.save
redirect_to root_path, :notice => "Bla bla bla"
end
Carrierwave provides a recreate_versions! method which will process and re-upload the image. In you case you can add a after_save callback that will trigger recreate_versions! method only if the rotate attribute is set to true.
In your model
#image.rb
after_save :rotate_image, if: ->(obj){ obj.rotate.present? and obj.rotate? }
def rotate_image
self.file.recreate_versions!
end
Now in your image uploader you can write the code to rotate a image.
#image_uploader.rb
.......
# It will replace the original image with rotated version
process :rotate_img, :if => model.rotate.present and model.rotate?
def rotate_img
manipulate! do |img|
img.rotate "90"
img #returns the manipulated image
end
end
If you don't want to replace the original image then all you have to do is to call process inside a version like
# Create different versions of your uploaded files:
version :rotated_img do
process :rotate_img
end
I have a validation method in my Photo Model, which, successfully makes the validation but afterwards when creating the photo gives me a strange error. I've tried to update Minimagick but It doesn't work. I know that without the validation it works correctly...
The Error
Errno::ENOENT in PhotosController#create
No such file or directory
Line in which the error is
image = MiniMagick::Image.open(photo.path)
Model
validate :validate_minimum_image_size
def validate_minimum_image_size
image = MiniMagick::Image.open(photo.path)
if image[:width] <= image[:height]
errors.add :photo, "should be 400x400px minimum!"
end
end
+Crop method*
def crop
if model.crop_x.present?
resize_to_limit(800, 500)
manipulate! do |img|
x = model.crop_x
y = model.crop_y
w = model.crop_w
h = model.crop_h
img.crop "#{w}x#{h}+#{x}+#{y}"
img
end
end
end
EDITED CONTROLLER
def create
#photo = Photo.new(photo_params)
#photo.user = current_user
if #photo.save
redirect_to [current_user, #photo], notice: 'El spot se ha subido correctamente!'
else
if /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.match(request.user_agent) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.match(request.user_agent[0..3])
redirect_to [current_user, #photo], notice: 'El spot se ha subido correctamente!'
else
render :crop
end
end
end
Update
I added the new validation approach in Photo model
private
def validate_minimum_image_size
if self.photo_upload_width <= self.photo_upload_height
errors.add :photo, "Dimensions of uploaded photo should be not less than 400x400 pixels."
end
end
end
But now, it's validating. If photo width <= photo height, it will send the photo to crop, otherwise, it will create it without cropping successfully. But there is a problem, when it sends the photo to crop, and I click on create, the following error is raised:
NoMethodError in PhotosController#create
undefined method `<=' for nil:NilClass
Extracted source (around line #37):
def validate_minimum_image_size
if self.photo_upload_width <= self.photo_upload_height
errors.add :photo, "Dimensions of uploaded photo should be not less than 400x400 pixels."
end
end
Since you're using Carrierwave, try this approach: https://gist.github.com/kirs/1239078
The parts you need to add/change look something like this:
Model
class Photo < ActiveRecord::Base
attr_accessor :photo_upload_width, :photo_upload_height
mount_uploader :photo, PhotoUploader
validate :validate_minimum_image_size
private
def validate_minimum_image_size
if self.photo_upload_width < 400 || self.photo_upload_height < 400
errors.add :photo, "Dimensions of uploaded photo should be not less than 400x400 pixels."
end
end
end
Uploader
class PhotoUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
# for image size validation
# fetching dimensions in uploader, validating it in model
before :cache, :capture_size_before_cache # callback, example here: http://goo.gl/9VGHI
def capture_size_before_cache(new_file)
if model.photo_upload_width.nil? || model.photo_upload_height.nil?
model.photo_upload_width, model.photo_upload_height = `identify -format "%wx %h" #{new_file.path}`.split(/x/).map { |dim| dim.to_i }
end
end
end
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.
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