Rails 4 várias imagens ou upload de arquivos usando carrierwave

Como posso carregar várias imagens de uma janela de seleção de arquivos usando o Rails 4 e o CarrierWave? Eu tenho um modelo post_attachments e post_attachments . Como posso fazer isso?

Alguém pode dar um exemplo? Existe uma abordagem simples para isso?

Esta é a solução para fazer upload de várias imagens usando carrierwave nos rails 4 a partir do zero

Ou você pode encontrar uma demonstração de trabalho: Multiple Attachment Rails 4

Para fazer basta seguir estes passos.

 rails new multiple_image_upload_carrierwave 

No arquivo gem

 gem 'carrierwave' bundle install rails generate uploader Avatar 

Criar post andaime

 rails generate scaffold post title:string 

Criar scaffold post_attachment

 rails generate scaffold post_attachment post_id:integer avatar:string rake db:migrate 

Em post.rb

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

Em post_attachment.rb

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

Em 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 

Em views / posts / _form.html.erb

 < %= form_for(@post, :html => { :multipart => true }) do |f| %> 
< %= f.label :title %>
< %= f.text_field :title %>
< %= f.fields_for :post_attachments do |p| %>
< %= p.label :avatar %>
< %= p.file_field :avatar, :multiple => true, name: "post_attachments[avatar][]" %>
< % end %>
< %= f.submit %>
< % end %>

Para editar um anexo e uma lista de anexos para qualquer postagem. Em visualizações / posts / show.html.erb

 

< %= notice %>

Title: < %= @post.title %>

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

Atualizar formulário para editar visualizações de anexos / post_attachments / _form.html.erb

 < %= image_tag @post_attachment.avatar %> < %= form_for(@post_attachment) do |f| %> 
< %= f.label :avatar %>
< %= f.file_field :avatar %>
< %= f.submit %>
< % end %>

Modifique o método de atualização em post_attachment_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 

Em rails 3 não é necessário definir parâmetros fortes e como você pode definir attribute_accessible no modelo e accept_nested_attribute para postar modelo porque o atributo accessible está obsoleto nos rails 4.

Para editar um anexo, não podemos modificar todos os anexos de cada vez. então vamos replace o anexo um por um, ou você pode modificar de acordo com sua regra, aqui eu apenas mostro como atualizar qualquer anexo.

Se dermos uma olhada na documentação da CarrierWave, isso é realmente muito fácil agora.

https://github.com/carrierwaveuploader/carrierwave/blob/master/README.md#multiple-file-uploads

Usarei o Produto como o modelo que quero adicionar as imagens, como exemplo.

  1. Obtenha a ramificação principal Carrierwave e adicione-a ao seu Gemfile:

     gem 'carrierwave', github:'carrierwaveuploader/carrierwave' 
  2. Crie uma coluna no modelo pretendido para hospedar uma matriz de imagens:

     rails generate migration AddPicturesToProducts pictures:json 
  3. Execute a migration

     bundle exec rake db:migrate 
  4. Adicionar fotos ao modelo Produto

     app/models/product.rb class Product < ActiveRecord::Base validates :name, presence: true mount_uploaders :pictures, PictureUploader end 
  5. Adicionar imagens a params fortes no ProductsController

     app/controllers/products_controller.rb def product_params params.require(:product).permit(:name, pictures: []) end 
  6. Permitir que seu formulário aceite várias fotos

     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. Em suas visualizações, você pode referenciar as imagens que analisam a matriz de imagens:

     @product.pictures[1].url 

Se você escolher várias imagens de uma pasta, o pedido será a ordem exata em que você está tirando de cima para baixo.

Algumas pequenas adições à resposta do SSR :

accept_nested_attributes_for não requer que você altere o controlador do object pai. Então, se corrigir

 name: "post_attachments[avatar][]" 

para

 name: "post[post_attachments_attributes][][avatar]" 

então todas essas mudanças de controlador como essas se tornam redundantes:

 params[:post_attachments]['avatar'].each do |a| @post_attachment = @post.post_attachments.create!(:avatar => a) end 

Além disso, você deve adicionar PostAttachment.new ao formulário de object pai:

Em views / posts / _form.html.erb

  < %= f.fields_for :post_attachments, PostAttachment.new do |ff| %> 
< %= ff.label :avatar %>
< %= ff.file_field :avatar, :multiple => true, name: "post[post_attachments_attributes][][avatar]" %>
< % end %>

Isso tornaria redundante essa alteração no controlador pai:

 @post_attachment = @post.post_attachments.build 

Para mais informações, veja o formulário Rails fields_for não sendo exibido, formulário nested

Se você usar o Rails 5, altere o valor de Rails.application.config.active_record.belongs_to_required_by_default de true para false (em config / initializers / new_framework_defaults.rb) devido a um bug dentro de accept_nested_attributes_for (caso contrário, accept_nested_attributes_for geralmente não funcionará no Rails 5) .

EDIT 1:

Para adicionar sobre destruir :

Nos modelos / post.rb

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

Em views / posts / _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?' } %> 

< % end %> < % end %>

Desta forma, você simplesmente não precisa ter o controlador de um object filho de forma alguma! Quero dizer, nenhum PostAttachmentsController é mais necessário. Quanto ao controlador do object pai ( PostController ), você também quase não o altera - a única coisa que você muda lá é a lista dos parâmetros whitelisted (para include os parâmetros relacionados ao object filho) assim:

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

É por isso que o accepts_nested_attributes_for é tão incrível.

Também descobri como atualizar o upload de vários arquivos e também refatorei um pouco. Este código é meu, mas você começa a deriva.

 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 

Aqui está o meu segundo refatorador no modelo:

  1. Mova methods privados para modelar.
  2. Substitua @ motherboard por si mesmo.

Controlador:

 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 

No modelo de placa-mãe:

 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 

Ao usar a associação @post.post_attachments você não precisa definir o post_id .