11

I have setup an uploader to upload a bunch of photos to an album. Then I let the user to select 4 images for the album cover. After they select it, I need to generate a smaller version of the photos. Only the selected 4. How can I do this?

Basically I need a way to generate image versions after the main upload. We can use something like refile. But the problem is, I also need it to create some versions while uploading!

THpubs
  • 7,804
  • 16
  • 68
  • 143

3 Answers3

9

Building off @karlingen's answer:

I'm going to assume there is a 'selected' boolean value on the table for the image records.

# picture.rb
belongs_to :my_model
scope :chosen, -> { where(selected: true) }

Set the version you want to display to the user to select in the Carrierwave Uploader, along with the versions you want after the album is selected

include CarrierWave::MiniMagick

# Original Version
version :thumb do
  process resize_to_fill: [50, 50]
end

# Processed Later
versions :large_album, if: :is_selected? do
  process resize_to_fill: [500, 500]
end
versions :medium_album, if: :is_selected? do
  process resize_to_fill: [400, 400]
end
versions :small_album, if: :is_selected? do
  process resize_to_fill: [300, 300]
end

private

# Check if it is selected
def is_selected?(picture)
  picture.uploader.model.selected
end

Conditional versions

Occasionally you want to restrict the creation of versions on certain properties within the model or based on the picture itself.

Then when the user selects the album photos they want to use you can call a manual create versions call in the controller on the selected albums:

my_model.pictures.chosen.each do |picture|
    picture.image.recreate_versions!(:large_album, :medium_album, :small_album)
end

Recreating versions

You might come to a situation where you want to retroactively change a version or add a new one. You can use the recreate_versions! method to recreate the versions from the base file. This uses a naive approach which will re-upload and process the specified version or all versions, if none is passed as an argument.

When you are generating random unique filenames you have to call save! on the model after using recreate_versions!. This is necessary because recreate_versions! doesn't save the new filename to the database. Calling save! yourself will prevent that the database and file system are running out of sync..... Or on a mounted uploader

You might also consider tying this into an after_update function.

blnc
  • 4,384
  • 1
  • 28
  • 42
2

Simply drop this into your uploader model:

include CarrierWave::MiniMagick

version :thumb do
  process :resize_to_fill => [50, 50]
end

Then inside your view, instead of calling my_model.picture you can do:

<%= image_tag my_model.picture_url(:thumb) if my_model.picture? %>
karlingen
  • 13,800
  • 5
  • 43
  • 74
2

You can use Cloudinary with carrierwave to do that:

Installation

To install Cloudinary was pretty simple. First add it and Carrier wave to your gemfile.

gem 'carrierwave'
gem 'cloudinary'

Then add the security settings for Cloudinary. These are found on your Cloudinary dashboard.

# config/cloudinary.yml

development:
  cloud_name: "sample"
  api_key: "874837483274837"
  api_secret: "a676b67565c6767a6767d6767f676fe1"

production:
  cloud_name: "sample"
  api_key: "874837483274837"
  api_secret: "a676b67565c6767a6767d6767f676fe1"

Now you are ready to add your CarrierWave uploader.

rails g uploader avatar

And add carrier wave to the new uploader. Also comment out the storage and store_dir lines as you aren't using local storage, but Cloudinary.

app/uploaders/avatar_uploader.rb

class AvatarUploader < CarrierWave::Uploader::Base

  include Cloudinary::CarrierWave

  # 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
  #   "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  # end

end

Super!

CarrierWave Setup

Now we can treat it like a normal CarrierWave setup. First add a migration to add the uploader to the model.

rails g migration add_avatar_to_user avatar:string
rake db:migrate

Now to add it into our model, form and view.

# app/models/user.rb
attr_accessible :avatar

mount_uploader :avatar, AvatarUploader


# app/views/users/_form.html.erb
<legend>Avatar</legend>
<%= f.file_field :avatar %>


# app/views/users/show.html.erb
<% unless @user.avatar_url.nil? %><%= image_tag @user.avatar_url %><br /><% end %>

Great! Now we are really rolling. You'll note that your images in the HTML pull from res.cloudinary.com rather than your app's URL, which is nice to save on bandwidth.

Transformations and Resizing

I also have gone and resized the images. I have two sizes, one for their profile and a mini one for when they show up in the users index. You can do all your transformations back in the image uploader. The :eager portion tells Cloudinary to do the resize at initial image save, rather than the first time the image is loaded.

# app/uploaders/avatar_uploader.rb

  version :display do
    process :eager => true
    process :resize_to_fill => [200, 200, :north]
  end

  version :thumbnail do
    process :eager => true
    process :resize_to_fit => [50, 50]
  end

You can then easily call the resized image from within the views.

<%= image_tag @user.avatar_url(:display) %>

Cloudinary has a host of other transformation features, both all that CarrierWave supports and some that it does not. For more info check out their documentation.

Have fun with all those images!