Rectangle 27 35

This is solution to upload multiple images using carrierwave in rails 4 from scratch

rails new multiple_image_upload_carrierwave
gem 'carrierwave'
bundle install
rails generate uploader Avatar
rails g scaffold post title:string
rails g scaffold post_attachment post_id:integer avatar:string

rake db:migrate
class Post < ActiveRecord::Base
   has_many :post_attachments
   accepts_nested_attributes_for :post_attachments
end
class PostAttachment < ActiveRecord::Base
   mount_uploader :avatar, AvatarUploader
   belongs_to :post
end
def show
   @post_attachments = @post.post_attachments.all
end

def new
   @post = Post.new
   @post_attachment = @post.post_attachments.build
end

def create
   @post = Post.new(post_params)

   respond_to do |format|
     if @post.save
       params[:post_attachments]['avatar'].each do |a|
          @post_attachment = @post.post_attachments.create!(:avatar => a, :post_id => @post.id)
       end
       format.html { redirect_to @post, notice: 'Post was successfully created.' }
     else
       format.html { render action: 'new' }
     end
   end
 end

 def update
   respond_to do |format|
     if @post.update(post_params)
       params[:post_attachments]['avatar'].each do |a|
         @post_attachment = @post.post_attachments.create!(:avatar => a, :post_id => @post.id)
       end
     end
  end

  def destroy
    @post.destroy
    respond_to do |format|
      format.html { redirect_to @post }
      format.json { head :no_content }
    end
  end


 private
   def post_params
      params.require(:post).permit(:title, post_attachments_attributes: [:id, :post_id, :avatar])
   end
<%= form_for(@post, :html => { :multipart => true }) do |f| %>
   <div class="field">
     <%= f.label :title %><br>
     <%= f.text_field :title %>
   </div>

   <%= f.fields_for :post_attachments do |p| %>
     <div class="field">
       <%= p.label :avatar %><br>
       <%= p.file_field :avatar, :multiple => true, name: "post_attachments[avatar][]" %>
     </div>
   <% end %>

   <% if params[:controller] == "post" && params[:action] == "edit" %> 
     <% @post.post_attachments.each do |p| %>
       <%= image_tag p.avatar, :size => "150x150" %>
     <% end %>
   <% end %>

   <div class="actions">
     <%= f.submit %>
   </div>
<% end %>
<p id="notice"><%= notice %></p>

<p>
  <strong>Title:</strong>
  <%= @post.title %>
</p>

<% @post_attachments.each do |p| %>
  <%= image_tag p.avatar_url, :size => "150x150" %>
  <%= link_to "Destroy", p, method: :delete %>
<% end %>

<%= link_to 'Edit', edit_post_path(@post) %> |
<%= link_to 'Back', posts_path %>

In rails 3 no need to define strong parameters and as you can define attribute_accessible in both the model and accept_nested_attribute to post model because attribute accessible is deprecated in rails 4.

Great post @SSR, only multiple upload I've managed to get working. Don't suppose you'd be able to expand it slightly? In it's current form the files aren't deleted when the parent model is. (Or don't for me), and I'm wondering how I'd go about editing the post and it's attachments together.

Essentially I'm wondering what the post_controller.rb would look like to correctly edit and delete the attachments?

thanks, it helps me I meant the idea!

Carrierwave, Rails 4, and Multiple Uploads - Stack Overflow

ruby-on-rails upload carrierwave
Rectangle 27 1

CarrierWave doesn't support multiple uploads. It's designed to associate a single file with a single field.

If you want multiple uploads, you need either multiple fields (each with a CarrierWave uploader), or multiple objects each with a single CarrierWave uploader field.

The multiple attribute is also unsupported, so if you use it, it's entirely up to you to get the parameters assigned properly.

That is not true. it support multiple upload within single field. see my answer. carrierwave provides much more.

Just to be clear: Your solution uses multiple objects (post_attachments), each with a single uploader, and demonstrates the manual work necessary to get parameters assigned properly for use of the "multiple" attribute. You've demonstrated how to do what the OP wanted, but it doesn't make any part of the above false.

Time progresses ever onwards. The Earth spins and gradually changes, and CarrierWave now supports multiple-file upload natively. However the CarrierWave way of doing it is an ugly hack (referencing all the files in one field using an array) that I would avoid using. Go with SSR's solution instead.

Carrierwave, Rails 4, and Multiple Uploads - Stack Overflow

ruby-on-rails upload carrierwave
Rectangle 27 177

This is solution to upload multiple images using carrierwave in rails 4 from scratch

rails new multiple_image_upload_carrierwave
gem 'carrierwave'
bundle install
rails generate uploader Avatar
rails generate scaffold post title:string
rails generate scaffold post_attachment post_id:integer avatar:string

rake db:migrate
class Post < ActiveRecord::Base
   has_many :post_attachments
   accepts_nested_attributes_for :post_attachments
end
class PostAttachment < ActiveRecord::Base
   mount_uploader :avatar, AvatarUploader
   belongs_to :post
end
def show
   @post_attachments = @post.post_attachments.all
end

def new
   @post = Post.new
   @post_attachment = @post.post_attachments.build
end

def create
   @post = Post.new(post_params)

   respond_to do |format|
     if @post.save
       params[:post_attachments]['avatar'].each do |a|
          @post_attachment = @post.post_attachments.create!(:avatar => a)
       end
       format.html { redirect_to @post, notice: 'Post was successfully created.' }
     else
       format.html { render action: 'new' }
     end
   end
 end

 private
   def post_params
      params.require(:post).permit(:title, post_attachments_attributes: [:id, :post_id, :avatar])
   end
<%= form_for(@post, :html => { :multipart => true }) do |f| %>
   <div class="field">
     <%= f.label :title %><br>
     <%= f.text_field :title %>
   </div>

   <%= f.fields_for :post_attachments do |p| %>
     <div class="field">
       <%= p.label :avatar %><br>
       <%= p.file_field :avatar, :multiple => true, name: "post_attachments[avatar][]" %>
     </div>
   <% end %>

   <div class="actions">
     <%= f.submit %>
   </div>
<% end %>

To edit an attachment and list of attachment for any post. In views/posts/show.html.erb

<p id="notice"><%= notice %></p>

<p>
  <strong>Title:</strong>
  <%= @post.title %>
</p>

<% @post_attachments.each do |p| %>
  <%= image_tag p.avatar_url %>
  <%= link_to "Edit Attachment", edit_post_attachment_path(p) %>
<% end %>

<%= link_to 'Edit', edit_post_path(@post) %> |
<%= link_to 'Back', posts_path %>
<%= image_tag @post_attachment.avatar %>
<%= form_for(@post_attachment) do |f| %>
  <div class="field">
    <%= f.label :avatar %><br>
    <%= f.file_field :avatar %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>
def update
  respond_to do |format|
    if @post_attachment.update(post_attachment_params)
      format.html { redirect_to @post_attachment.post, notice: 'Post attachment was successfully updated.' }
    end 
  end
end

In rails 3 no need to define strong parameters and as you can define attribute_accessible in both the model and accept_nested_attribute to post model because attribute accessible is deprecated in rails 4.

For edit an attachment we cant modify all the attachments at a time. so we will replace attachment one by one, or you can modify as per your rule, Here I just show you how to update any attachment.

in the show action of the post controller i think you've forgot @post =Post.find(params[:id])

@SSR Why you looping through each post attachments in create action? Rails and carrierwave are smart enough to save collections automatically.

:_destroy

When I add validations to the post_attachment model, they do not prevent the post model from saving. Instead the post is saved, and then the ActiveRecord invalid error is thrown for the attachment model only. I think this is because of the create! method. but using create instead just fails silently. Any idea how to have the validation happen on the post reach into the attachments?

Rails 4 multiple image or file upload using carrierwave - Stack Overfl...

ruby-on-rails-4 carrierwave
Rectangle 27 6

Also I figured out how to update the multiple file upload and I also refactored it a bit. This code is mine but you get the drift.

def create
  @motherboard = Motherboard.new(motherboard_params)
  if @motherboard.save
    save_attachments if params[:motherboard_attachments]
    redirect_to @motherboard, notice: 'Motherboard was successfully created.'
  else
    render :new
  end
end


def update
  update_attachments if params[:motherboard_attachments]
  if @motherboard.update(motherboard_params)
    redirect_to @motherboard, notice: 'Motherboard was successfully updated.'
  else
   render :edit
  end
end

private
def save_attachments
  params[:motherboard_attachments]['photo'].each do |photo|
    @motherboard_attachment = @motherboard.motherboard_attachments.create!(:photo => photo)
  end
end

 def update_attachments
   @motherboard.motherboard_attachments.each(&:destroy) if @motherboard.motherboard_attachments.present?
   params[:motherboard_attachments]['photo'].each do |photo|
     @motherboard_attachment = @motherboard.motherboard_attachments.create!(:photo => photo)
   end
 end

Thanks for sharing your code. when you get time please update code at my gihub repo and do not forget to comment for each method so everyone can easily understand code.

Have you had a chance to give me access?

Rails 4 multiple image or file upload using carrierwave - Stack Overfl...

ruby-on-rails-4 carrierwave
Rectangle 27 27

If we take a look at CarrierWave's documentation, this is actually very easy now.

I will use Product as the model I want to add the pictures, as an example.

Get the master branch Carrierwave and add it to your Gemfile:

gem 'carrierwave', github:'carrierwaveuploader/carrierwave'

Create a column in the intended model to host an array of images:

rails generate migration AddPicturesToProducts pictures:json
  • Run the migration bundle exec rake db:migrate
app/models/product.rb

class Product < ActiveRecord::Base
  validates :name, presence: true
  mount_uploaders :pictures, PictureUploader
end

Add pictures to strong params in ProductsController

app/controllers/products_controller.rb

def product_params
  params.require(:product).permit(:name, pictures: [])
end
  • Allow your form to accept multiple pictures app/views/products/new.html.erb # notice 'html: { multipart: true }' <%= form_for @product, html: { multipart: true } do |f| %> <%= f.label :name %> <%= f.text_field :name %> # notice 'multiple: true' <%= f.label :pictures %> <%= f.file_field :pictures, multiple: true, accept: "image/jpeg, image/jpg, image/gif, image/png" %> <%= f.submit "Submit" %> <% end %>

In your views, you can reference the images parsing the pictures array:

@product.pictures[1].url

If you choose several images from a folder, the order will be the exact order you are taking them from top to bottom.

CarrierWave's solution to this problem makes me cringe. It involves putting all the references to the files into one field in an array! It certainly wouldn't be considered the "rails way". What if you then want to remove some, or add extra files to the post? I'm not saying it wouldn't be possible, I'm just saying it would be ugly. A join table is a much better idea.

That solutions is already provided by SSR. Another model is put in place to hold the uploaded file, then the thing that needs many files uploaded relates in a one-to-many or many-to-many relationship with that other model. (the join table I mentioned in my earlier comment would be in the case of a many-to-many relationship)

Thanks @Toby1Kenobi, I was wondering how the column array method would account for image versions (I don't see how it can). Your strategy is doable.

Rails 4 multiple image or file upload using carrierwave - Stack Overfl...

ruby-on-rails-4 carrierwave
Rectangle 27 4

accepts_nested_attributes_for does not require you to change the parent object's controller. So if to correct

name: "post_attachments[avatar][]"
name: "post[post_attachments_attributes][][avatar]"
params[:post_attachments]['avatar'].each do |a|
  @post_attachment = @post.post_attachments.create!(:avatar => a)
end
PostAttachment.new
<%= f.fields_for :post_attachments, PostAttachment.new do |ff| %>
    <div class="field">
      <%= ff.label :avatar %><br>
      <%= ff.file_field :avatar, :multiple => true, name: "post[post_attachments_attributes][][avatar]" %>
    </div>
  <% end %>

This would make redundant this change in the parent's controller:

@post_attachment = @post.post_attachments.build
Rails.application.config.active_record.belongs_to_required_by_default
true
false
class Post < ApplicationRecord
    ...
    accepts_nested_attributes_for :post_attachments, allow_destroy: true
end
<% f.object.post_attachments.each do |post_attachment| %>
    <% if post_attachment.id %>

      <%

      post_attachments_delete_params =
      {
      post:
        {              
          post_attachments_attributes: { id: post_attachment.id, _destroy: true }
        }
      }

      %>

      <%= link_to "Delete", post_path(f.object.id, post_attachments_delete_params), method: :patch, data: { confirm: 'Are you sure?' } %>

      <br><br>
    <% end %>
  <% end %>

This way you simply do not need to have a child object's controller at all! I mean no any PostAttachmentsController is needed anymore. As for parent object's controller (PostController), you also almost don't change it - the only thing you change in there is the list of the whitelisted params (to include the child object-related params) like this:

def post_params
  params.require(:post).permit(:title, :text, 
    post_attachments_attributes: ["avatar", "@original_filename", "@content_type", "@headers", "_destroy", "id"])
end
accepts_nested_attributes_for

Those are actually major additions to @SSR answer, not minor :) accept_nested_attributes_for is quite something. Indeed there's no need for a child controller at all. By following your approach, the only thing I'm unable to do is to display form error messages for the child when something goes wrong with the upload.

Rails 4 multiple image or file upload using carrierwave - Stack Overfl...

ruby-on-rails-4 carrierwave
Rectangle 27 2

Here is my second refactor into the model:

  • Replace @motherboard with self.
def create
  @motherboard = Motherboard.new(motherboard_params)

  if @motherboard.save
    @motherboard.save_attachments(params) if params[:motherboard_attachments]
  redirect_to @motherboard, notice: 'Motherboard was successfully created.'
  else
    render :new
  end
end

def update
  @motherboard.update_attachments(params) if params[:motherboard_attachments]
  if @motherboard.update(motherboard_params)
    redirect_to @motherboard, notice: 'Motherboard was successfully updated.'
  else
    render :edit
  end
end
def save_attachments(params)
  params[:motherboard_attachments]['photo'].each do |photo|
    self.motherboard_attachments.create!(:photo => photo)
  end
end

def update_attachments(params)
  self.motherboard_attachments.each(&:destroy) if self.motherboard_attachments.present?
  params[:motherboard_attachments]['photo'].each do |photo|
    self.motherboard_attachments.create!(:photo => photo)
  end
end

Rails 4 multiple image or file upload using carrierwave - Stack Overfl...

ruby-on-rails-4 carrierwave
Rectangle 27 2

When using the association @post.post_attachments you do not need to set the post_id.

Rails 4 multiple image or file upload using carrierwave - Stack Overfl...

ruby-on-rails-4 carrierwave
Rectangle 27 4

I had the same problem and I found that you have to call 'remove_images!' if this was the last one. In 'remove_image_at_index' function add:

@gallery.remove_images! if remain_images.empty?

Looks plausible. Using Paperclip for my new app and won't be able to check this out anytime soon. I"ll try and think of it when I do, thanks anyway.

arrays - Rails 5 CarrierWave, can't remove last file in a multiple upl...

arrays carrierwave ruby-on-rails-5
Rectangle 27 2

if it is okay you can check these solutions:

Paperclip works well with images that need to be associated with models. But what if you're not working with images and don't want file uploads to be persisted or associated with a model? Is there no easy way to handle that case?

Rails 4 multiple file uploads solution - Stack Overflow

ruby-on-rails file-upload ruby-on-rails-4
Rectangle 27 2

if it is okay you can check these solutions:

Paperclip works well with images that need to be associated with models. But what if you're not working with images and don't want file uploads to be persisted or associated with a model? Is there no easy way to handle that case?

Rails 4 multiple file uploads solution - Stack Overflow

ruby-on-rails file-upload ruby-on-rails-4
Rectangle 27 1

Add the relevant attributes to your model and introduce a before_save callback.

class Video < ActiveRecord::Base
  mount_uploader :video, VideoUploader

  before_save :update_video_attributes
  private

  def update_video_attributes
    if video.present? && video_changed?
      self.content_type = video.file.content_type
      self.file_size = video.file.size
    end
  end
end

Can I do this in carrierwave uploader too? do I have to set for each element a special column in table?

ruby on rails - carrierwave multiple file uploads and storing - Stack ...

ruby-on-rails ruby ruby-on-rails-4 carrierwave
Rectangle 27 2

I never found a solution to uploading multiple images in a nested form with the jQuery file upload. After all the research I've done I don't think it is posible.

In the end I followed railscast #196 & was able to upload multiple images at a time with carrierwave & javascript.

ruby on rails - multiple image uploads in a nested form with carrierwa...

ruby-on-rails ruby-on-rails-3 carrierwave nested-forms railscasts
Rectangle 27 2

As per the error logs, you have not permitted image to be saved in the database. In your FilmesController, you need to permit :image as

def filme_params
  params.require(:filme).permit(:name, :moviestype, :image) ## Add :image attribute
end
params.fetch(:fileme, {})

ruby - CarrierWave File Uploads not working in rails - Stack Overflow

ruby-on-rails ruby ruby-on-rails-4 carrierwave
Rectangle 27 12

<%= form_for @photo, :html => {:multipart => true}  do |f| %>
  <% if @photo.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@photo.errors.count, "error") %> prohibited this photo from being saved:</h2>

      <ul>
      <% @photo.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <fieldset>
    <legend>Upload a Photo</legend>
    <div class="field">
      <%= f.file_field :image %>
    </div>              
   </fieldset>  

   <%= f.submit "Upload Photo", :class => "btn btn-small" %>
<% end %>

Now there this is not a problem with rails or carrierwave or strong_parameter it is something how html works . It like this, if you have file input and if nothing is attached to it the name and value pair is not sent to the server by HTML think of it something like checkbox or disabled field

Now since your form only contain <%= f.file_field :image %> and it does not contain any other field (attributes of photo model)

therefore the photo hash would not get constructed when the file input does not contain any attachment

which is evident in your log as well

{"utf8"=>"", "authenticity_token"=>"IvOieBvxdA6qzVKrt1dYBuCXlt+uuWCyWjUAuTK0XEU=", "commit"=>"Upload Photo"}
{"utf8"=>"", "authenticity_token"=>"I4O0w+Wk8nJaD6HJRSC+FfuAip5NVE6TkCUFDEN+sW0=", "photo"=>{"image"=>#<ActionDispatch::Http::UploadedFile:0x007fc730b5de08 @tempfile=#<Tempfile:/var/folders/0m/n32lgww55vzf5pfc4kh17gm00000gr/T/RackMultipart20130801-32602-1nj6u2b>, @original_filename="bag.jpg", @content_type="image/jpeg", @headers="Content-Disposition: form-data; name=\"photo[image]\"; filename=\"bag.jpg\"\r\nContent-Type: image/jpeg\r\n">}, "commit"=>"Upload Photo"}

now you can see the difference between the two and that the difference is the reason of your error because in

params[:photo].present?
params.require(:photo).permit(:image)

because of the line mention that .require(:photo) which is not present in params

Perhaps you can do something like this

def photo_params
     if params[:photo].present?
      params.require(:photo).permit(:image)
     end
   end

It not anyone fault because that is how the HTML work if no attachment no name=value pair submitted to server and because of which photo params is not sent to server hence the hash is does not have them and hence strong parameter throws the error

This is very helpful and gets at the heart of the problem, but I think I need to accept Mrjaco12's answer as a more canonical approach since it's documented in the rails guides. Thanks for your help though!

what if we need "if photo_params[:image].present?" in this case photo_params is false, and [:image] hash is nil. so we cant check for present?

@Kirka121 Sorry man I seriously don't remember much on this but just a question how come photo_params will be false

Rails 4 strong parameters param not found error with carrierwave - Sta...

ruby-on-rails carrierwave ruby-on-rails-4 strong-parameters
Rectangle 27 1

This worked with the nested attributes:

params.permit( ..., :photos_attributes => ['id', 'title', 'image', '_destroy'])

But looks like either Carrierwave or Nested Form should be updated for Rails 4 first. It simply tries to save an empty image all the time which I kinda eliminated with :reject_if, but still it is not 100 working.

File upload with Rails 4, Strong Parameters and Carrierwave - Stack Ov...

file-upload carrierwave nested-forms ruby-on-rails-4 strong-parameters
Rectangle 27 34

So there are a few issues here.

First, Paperclip's has_attached_file method isn't an association to many files. It looks like you're trying to build an "asset" as if it's a Rails association. All Paperclip does is put a couple of fields into your table to store some meta-data about the file and you get one attached file per declaration of has_attached_file. If you want to attach 5 files, you would need to do something like:

has_attached_file :asset1
has_attached_file :asset2
has_attached_file :asset3
has_attached_file :asset4
has_attached_file :asset5

OR, alternatively, you could create another model just to store the files. For example:

class Listing < ActiveRecord::Base
  has_many :assets
end

class Asset < ActiveRecord::Base
  belongs_to :listing
  has_attached_file :picture
end

That way, you could have multiple assets attached to one listing (you didn't say what the original object was so I just called it "listing").

Second, there is no such thing as a multiple file upload in HTML (and, as such, the file_field method doesn't take a :multiple => true argument. You'll have to use something beyond Rails built-in form handling if you want multiple-file upload. Uploadify is a decent choice (that I've used before). There is a gem that will transform file fields to use uploadify (and will support the :multiple => true syntax that you want): https://github.com/mateomurphy/uploadify_rails3/wiki. However, I cannot vouch for how good it is.

My advice would be to start step-by-step. Uploading via Flash to Rails can be a complicated process that involves dealing with the CSRF meta-tag and other fields in your form. Start by making a form that allows a user to upload one file and stores it through Paperclip. Then maybe break the has_attached_file declaration into another model so that you can have 1 or many files associated with a model (as shown in the multi-model code block above). Then try adding Uploadify or another alternative. Ernie Miller has a decent tutorial on integrating Uploadify: http://erniemiller.org/2010/07/09/uploadify-and-rails-3/.

To start, remember that has_attached_file can only attach one file. When you try calling @listing.assets there is no "assets". There is an asset. You need to create a separate model yourself and use Rails' associations if you want multiple files.

I already do have another model created called "Listing". A listing has_many assets. I already can upload multiple assets, just not through a single "Choose File" field. if anyone has any good examples using uploadify that'd be great. I'm not sure how to do the background JS to process each image and POST them to the server properly.

You can add :multiple => "multiple" to your file_field, that enables multiple file selection from one html file input field (at least in Chrome). I haven't solved the problem of managing these uploads properly yet, but I know that post parameter includes an array of the multiple files you have picked and uploaded.

:html => {:multipart => :true }
form_for

Just to add to this: i would move the file uploading to ajax, allowing you to gather a list of files from the user and then upload them one at a time in the background: this way it would appear to the user that they have uploaded lots of files at once, even though behind the scenes that's not what's happening. You can also set any other params for how the resulting records might need to be organised on the server. For file upload with ajax (jquery), which can be a bit tricky to get right, see eg abandon.ie/notebook/simple-file-uploads-using-jquery-ajax

Rails Paperclip & Multiple File Uploads - Stack Overflow

ruby-on-rails file-upload amazon-s3 paperclip
Rectangle 27 2

Accepted answer says there is no such thing as a multiple file upload in HTML.

<%= f.file_field :files, multiple: true %>

This allows you to select multiple images and send them as an array.

If you have the relationship Dog has_many Images and Image has_attachment :file, do this to get multiple images to upload at once:

<%= form_for @dog, html: { multipart: true } do |f| %>
  <%= f.file_field :files, accept: 'image/png,image/jpeg,image/gif', multiple: true %>
<%= end %>
def dog_params
  params.require(:dog).permit files: []
end
def files=(array = [])
  array.each do |f|
    images.create file: f
  end
end

Rails Paperclip & Multiple File Uploads - Stack Overflow

ruby-on-rails file-upload amazon-s3 paperclip
Rectangle 27 11

You may be seeing this error message because Carrierwave hasn't been initialized in your model.

The carrierwave method mount_uploader needs to be added to the image field in the model like this:

class Item < ActiveRecord::Base
  mount_uploader :image, ImageUploader
end

why when i'm upload image using carrierwave in rails 4 database is rol...

ruby-on-rails-4 carrierwave strong-parameters
Rectangle 27 3

<%= file_field_tag :file, multiple: true %>
<%= file_field_tag 'files[]', multiple: true %>

Thanks. After doing that, I just needed to change the backend to deal with an array (as opposed to the repeated JS calls the railscast has).

How do I upload multiple files in Rails 4 (with jquery)? - Stack Overf...

jquery ruby-on-rails file-upload ruby-on-rails-4