Rails 4使用 carrierwave 上传多个图像或文件

如何使用 Rails 4和 CarrierWave 从文件选择窗口上传多个图像?我有一个 post_controllerpost_attachments型号。我怎么能这么做?

有人能举个例子吗? 有没有一个简单的方法来解决这个问题?

74833 次浏览

这是解决方案上传多个图像使用载波在轨道4从头开始

或者你可以找到工作演示: 多重连接 Rails 4

要做的只是遵循以下步骤。

rails new multiple_image_upload_carrierwave

在 gem 文件中

gem 'carrierwave'
bundle install
rails generate uploader Avatar

创建支撑脚手架

rails generate scaffold post title:string

创建 post _ attachold

rails generate scaffold post_attachment post_id:integer avatar:string


rake db:migrate

在后 rb

class Post < ActiveRecord::Base
has_many :post_attachments
accepts_nested_attributes_for :post_attachments
end

在 post _ atting.rb 中

class PostAttachment < ActiveRecord::Base
mount_uploader :avatar, AvatarUploader
belongs_to :post
end

在 post _ controller. rb 中

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

在 views/post/_ form.html.erb 中

<%= 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 %>

为任何文章编辑附件和附件列表。 在 views/post/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 %>

更新表单以编辑附件 Views/post _ attachments/_ form.html.erb

<%= 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 %>

修改 Post _ attance _ controller. rb中的更新方法

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

在 Rails 3中不需要定义强参数,因为您可以在模型中定义 Attribute _ access,并在 post 模型中接受 _ nested _ Attribute,因为在 Rails 4中不推荐使用 Attribute access。

对于编辑附件,我们不能一次修改所有的附件。因此,我们将一个接一个地替换附件,或者您可以根据您的规则进行修改,这里我只是向您展示如何更新任何附件。

使用关联 @post.post_attachments时,不需要设置 post_id

此外,我想出了如何更新多个文件上传,我也重构了一点。这个密码是我的,但你明白我的意思。

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

下面是我对模型的第二个重构:

  1. 将私有方法移动到建模。
  2. 用 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

如果我们看一下 CarrierWave 的文档,现在实际上非常简单。

Https://github.com/carrierwaveuploader/carrierwave/blob/master/readme.md#multiple-file-uploads

我将使用 Product 作为我想添加图片的模型,作为一个示例。

  1. 获取主分支 Carrierwave 并将其添加到 Gemfile:

    gem 'carrierwave', github:'carrierwaveuploader/carrierwave'
    
  2. Create a column in the intended model to host an array of images:

    rails generate migration AddPicturesToProducts pictures:json
    
  3. Run the migration

    bundle exec rake db:migrate
    
  4. Add pictures to model Product

    app/models/product.rb
    
    
    class Product < ActiveRecord::Base
    validates :name, presence: true
    mount_uploaders :pictures, PictureUploader
    end
    
  5. Add pictures to strong params in ProductsController

    app/controllers/products_controller.rb
    
    
    def product_params
    params.require(:product).permit(:name, pictures: [])
    end
    
  6. 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 %>
    
  7. 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.

战略科学军团答案的一些小小补充:

> 不需要您更改父对象的控制器

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添加到父对象表单:

在 views/post/_ form.html.erb 中

  <%= 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 %>

这会使父控制器中的这个更改变得多余:

@post_attachment = @post.post_attachments.build

更多信息见 Rails fields _ for 表单未显示,嵌套表单

如果您使用 Rails 5,那么将 Rails.application.config.active_record.belongs_to_required_by_default值从 true更改为 false(在 config/initializer/new _ Framework _ default 中)。Rb) ,因为 接受 _ nested _ properties _ for内部存在 bug (否则 接受 _ nested _ properties _ for通常不能在 Rails 5下工作)。

编辑1:

关于 毁灭的补充:

在 model/post.rb 中

class Post < ApplicationRecord
...
accepts_nested_attributes_for :post_attachments, allow_destroy: true
end

在 views/post/_ form.html.erb 中

 <% 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 %>

这样,您就可以简单地 不需要拥有一个子对象的控制器了!我的意思是不再需要 PostAttachmentsController了。至于父对象的控制器(PostController) ,你也不需要改变它——你唯一需要改变的是白名单中的参数列表(包括子对象相关的参数) ,如下所示:

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如此神奇。