¶ ↑
Upload multiple files to Cloudinary with CarrierwaveDEMO take a look before you start:
damp-brushlands-82606.herokuapp.com/
¶ ↑
IntroductionCloudinary is a cloud-based service that provides an end-to-end image management solution including uploads, storage, administration, image manipulation, and delivery.
Cloudinary GEM provides a plugin for CarrierWave. Using it means that you enjoy the benefits of CarrierWave for easily uploading images from HTML forms to your model, while enjoying the great benefits of Cloudinary: uploaded images are stored in cloud, transformed and manipulated in the cloud, and delivered automatically through a CDN.
¶ ↑
Getting startedLike always we start by generating new rails app. In this app I use for database: gem ‘sqlite3’ in development and gem “pg” (because Heroku doesn’t work with sqlite3) in environment and testing.
Next, we generate two models: rails g scaffold Author name:string and rails g model Image author_id:integer image:string. First model is generated using scaffold, which is a quick way to generate some of the major pieces of an application. Second command generate just empty image.rb file.
Generated Author model should look similar to this:
author.rb class Author < ActiveRecord::Base has_many :images, :dependent => :destroy accepts_nested_attributes_for :images, allow_destroy: true end
First, in Author’s model we see that association have been used with Image model, where author has_many images.
Second, we use accepts_nested_attributes_for :images so we can use fields_for helper in view, which allows us to add “Image model fields in Author’s model view”. Fields_for helper sort of nested with form_for helper.
Accepts_nested_attributes_for requires us to take some more actions. Make sure to nest strong params to be able to manipulate associated records.
authors_controller.rb def author_params params.require(:author).permit(:name, images_attributes: [:image, :author_id]) end
We also have to use build method just to create a new object in memory so that the view can take this object and display something, especially for a form.
authors_controller.rb def new @author = Author.new @image = @author.images.build end
Here comes most important part of this article. While creating author we have to make sure that associated model, which is Image in our case is created as well. We do that by cheking if params present and iterate through it in create action. The same applies for update action:
authors_controller.rb def create @author = Author.new(author_params) respond_to do |format| if @author.save if params[:images] params[:images].each do |image| @author.images.create(image: image) end end format.html { redirect_to @author, notice: 'Author was successfully created.' } format.json { render action: 'show', status: :created, location: @author } else format.html { render action: 'new' } format.json { render json: @author.errors, status: :unprocessable_entity } end end end
Second Image model should look similar to this:
image.rb class Image < ActiveRecord::Base mount_uploader :image, ImageUploader belongs_to :author end
Later on we should be able to delete images we don’t like. To do that we have to generate Image controller and add destroy action:
rails g controller Images destroy def destroy @image = Image.find(params[:id]) @image.destroy respond_to do |format| format.html { redirect_to :back, notice: 'Image has been deleted' } format.json { head :no_content } format.js { render layout: false} end end
And in routes.rb add following line if you find it necessary:
routes.rb resources :images, only: [:destroy]
We are still missing some views to see how it works on the screen, however you can experinment with rails console and check if it works as expected. Before adding views this article will describe on how to integrate Carrierwave and Cloudinary.
¶ ↑
Carrierwave integrationIntegration of Carrierwave is pretty much straightforward and is well documented. github.com/carrierwaveuploader/carrierwave
Note: The Carrierwave gem should be loaded before the Cloudinary gem in your Gemfile.
gem 'carrierwave' gem 'cloudinary'
Start off by generating Carrierwave uploader:
rails generate uploader Image
This should give you a file in:
app/uploaders/image_uploader.rb
Customize your newly uploader so it should look similar to this:
class ImageUploader < CarrierWave::Uploader::Base include Cloudinary::CarrierWave version :authors_image do process :resize_to_fill => [60, 60] end def extension_white_list %w(jpg jpeg gif png) end end
Open your model file (in this case image.rb) and mount the uploader if not yet mounted:
class Image < ActiveRecord::Base mount_uploader :image, ImageUploader end
That’s it. Carrierwave is intergrated. Now let us do Cloudinary part.
¶ ↑
Cloudinary integrationCloudinary is a great service if you need to deal with images. Sign up for free account and use your free quota, which is enough to get you started. You need to find on newly created account cloud_name, api_key and api_secret to perform secure API calls to Cloudinary. More info cloudinary.com/documentation/rails_integration
Under the config directory create a cloudinary.yml configuration file and store cloud_name, api_key and api_secret. Your cloudinary.yml should look similar to this:
cloudinary.yml --- development: cloud_name: cloud_name api_key: 'api_key' api_secret: api_secret enhance_image_tag: true static_image_support: false secure: true production: cloud_name: cloud_name api_key: 'api_key' api_secret: api_secret enhance_image_tag: true static_image_support: true secure: true test: cloud_name: cloud_name api_key: 'api_key' api_secret: api_secret enhance_image_tag: true static_image_support: false secure: true
¶ ↑
Finally ViewsLast part of this article. All our logic is concentrated in views, without them would be hard to comprehend how it’s actually feels like to upload an image to cloud service. We don’t have any views for Image model because we will use fields_for helper, which with the help of accets_nested_attributes_for method gives us ability to save all records from one view. Add to new.html.erb file:
<%= form_for(@author, :html => { multipart: true }) do |f| %> ... <%= f.fields_for :image do |ff| %> <div class="field"> <%= ff.label :image %><br> <%= ff.file_field :image, multiple: true, name: "images[]" %> </div> <% end %> ... <% end %>
Below is a link to delete an image:
<% @author.images.each do |image| %> <%= image_tag image.image_url(:authors_image) %><br> <%= link_to "Delete", image_path(image), :method => :delete %> <% end %>
That’s pretty much it. You now should be able to upload multiple files to Cloudinary with Carrirwave. Feel free to initiate an issue if you have any problems. Bey!