Rails Paperclip & Multiple File Uploads
Here is a full example of multiple file uploads. Here a user has_many uploads
. Each upload
model has an avatar
which represents the file attachment. Ultimately: we are creating many uploads
when we create the user
.
The Models
#models/user.rb
class User < ApplicationRecord
has_many :uploads
def files=(array_of_files = [])
array_of_files.each do |f|
uploads.build(avatar: f, user: self)
end
end
end
#models/upload.rb
class Upload < ApplicationRecord
belongs_to :user
has_attached_file :avatar
validates_attachment_content_type :avatar, :content_type => ["image/png"]
end
The form:
# views/users/_form.html.erb
<%= form_with(model: user, local: true) do |form| %>
...
<div class="field">
<%= form.file_field :files, multiple: true %>
</div>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
The Controller
class UsersController < ApplicationController
before_action :set_user, only: [:show]
def show
end
def new
@user = User.new
end
def create
@user = User.new(user_params)
if @user.save
redirect_to @user, notice: 'User was successfully created.'
end
end
private
def set_user
@user = User.find(params[:id])
end
def user_params
params.require(:user).permit(:name, files: [])
end
end
User#show
<p id="notice"><%= notice %></p>
<p>
<strong>Name:</strong>
<%= @user.name %>
</p>
<h3>Uploads</h3>
<div>
<% @user.uploads.each do |upload| %>
<div>
<%= link_to upload.avatar.url do%>
<%= upload.avatar_file_name %>
<% end %>
</div>
<% end %>
</div>
<%= link_to 'Edit', edit_user_path(@user) %> |
<%= link_to 'Back', users_path %>
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:
In your html.erb
<%= form_for @dog, html: { multipart: true } do |f| %>
<%= f.file_field :files, accept: 'image/png,image/jpeg,image/gif', multiple: true %>
<%= end %>
In your controller
def dog_params
params.require(:dog).permit files: []
end
In your Dog model
def files=(array = [])
array.each do |f|
images.create file: f
end
end
This is assuming you're already able to upload one image but want to upgrade to multiple images at once. Notice that wait time will increase.
To help reduce wait time, peep my post on this question related to speed uploading.
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.