Carrierwave를 사용한 Rails 4 다중 이미지 또는 파일 업로드


86

Rails 4 및 CarrierWave를 사용하여 파일 선택 창에서 여러 이미지를 업로드하려면 어떻게해야합니까? 나는 post_controllerpost_attachments모델이 있습니다. 어떻게 할 수 있습니까?

누군가 예를 들어 줄 수 있습니까? 이것에 대한 간단한 접근 방식이 있습니까?

답변:


195

이것은 처음부터 레일 4의 반송파를 사용하여 여러 이미지를 업로드하는 솔루션입니다.

또는 작동 데모를 찾을 수 있습니다. Multiple Attachment Rails 4

수행하려면 다음 단계를 따르십시오.

rails new multiple_image_upload_carrierwave

gem 파일에서

gem 'carrierwave'
bundle install
rails generate uploader Avatar 

포스트 스캐 폴드 만들기

rails generate scaffold post title:string

post_attachment 스캐 폴드 만들기

rails generate scaffold post_attachment post_id:integer avatar:string

rake db:migrate

post.rb에서

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

post_attachment.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 / posts / _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 / 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 %>

첨부 파일 보기 /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_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

rails 3에서는 강력한 매개 변수를 정의 할 필요가 없으며, rails 4에서는 접근 가능한 속성이 더 이상 사용되지 않기 때문에 모델에서 attribute_accessible을 정의하고 모델을 게시하기 위해 accept_nested_attribute를 정의 할 수 있습니다.

첨부 파일을 편집하기 위해 한 번에 모든 첨부 파일을 수정할 수 없습니다. 첨부 파일을 하나씩 교체하거나 규칙에 따라 수정할 수 있습니다. 여기에서는 첨부 파일을 업데이트하는 방법을 보여 드리겠습니다.


2
포스트 컨트롤러의 쇼 액션에서 나는 당신이 @post = Post.find (params [: id])를 잊었다 고 생각합니다
wael

1
@SSR 왜 각 게시물 첨부 파일을 반복적으로 반복 create합니까? Rails와 carrierwave는 컬렉션을 자동으로 저장할 수있을만큼 똑똑합니다.

3
편집을보고 싶어요 (특히 처리 :_destroy부분)
Tun

5
@SSR-귀하의 답변은 매우 유용합니다. 수정 작업으로 답변을 업데이트 해 주시겠습니까?
raj_on_rails 2015 년

2
post_attachment 모델에 유효성 검사를 추가해도 게시 모델이 저장되는 것을 막지는 않습니다. 대신 게시물이 저장되고 첨부 모델에 대해서만 ActiveRecord 유효하지 않은 오류가 발생합니다. 나는 이것이 창조 때문이라고 생각한다! 방법. 그러나 대신 create를 사용하면 자동으로 실패합니다. 게시물에 대한 유효성 검사가 첨부 파일에 도달하는 방법에 대한 아이디어가 있습니까?
dchess

32

CarrierWave의 문서를 살펴보면 실제로 매우 쉽습니다.

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

사진을 추가하고 싶은 모델로 제품을 예로 들어 보겠습니다.

  1. 마스터 브랜치 Carrierwave를 가져 와서 Gemfile에 추가합니다.

    gem 'carrierwave', github:'carrierwaveuploader/carrierwave'
    
  2. 이미지 배열을 호스팅하기 위해 의도 한 모델에 열을 만듭니다.

    rails generate migration AddPicturesToProducts pictures:json
    
  3. 마이그레이션 실행

    bundle exec rake db:migrate
    
  4. 모델 제품에 사진 추가

    app/models/product.rb
    
    class Product < ActiveRecord::Base
      validates :name, presence: true
      mount_uploaders :pictures, PictureUploader
    end
    
  5. ProductsController의 강력한 매개 변수에 그림 추가

    app/controllers/products_controller.rb
    
    def product_params
      params.require(:product).permit(:name, pictures: [])
    end
    
  6. 양식에서 여러 사진을 허용하도록 허용

    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. 뷰에서 pictures 배열을 구문 분석하는 이미지를 참조 할 수 있습니다.

    @product.pictures[1].url
    

폴더에서 여러 이미지를 선택하는 경우 순서는 위에서 아래로 가져 오는 정확한 순서가됩니다.


9
이 문제에 대한 CarrierWave의 솔루션은 저를 절망하게 만듭니다. 파일에 대한 모든 참조를 배열의 한 필드에 넣는 것이 포함됩니다! 확실히 "레일 방식"으로 간주되지 않을 것입니다. 그런 다음 일부를 제거하거나 게시물에 추가 파일을 추가하려면 어떻게해야합니까? 나는 그것이 가능하지 않다고 말하는 것이 아니라 단지 추악 할 것이라고 말하는 것입니다. 조인 테이블이 훨씬 더 나은 아이디어입니다.
Toby 1 Kenobi

3
나는 더 많은 토비에 동의 할 수 없었다. 그 솔루션을 친절하게 제공 하시겠습니까?
drjorgepolanco

2
해당 솔루션은 이미 SSR에서 제공합니다. 업로드 된 파일을 보관하기 위해 또 다른 모델이 배치되고, 업로드 된 많은 파일이 필요한 것은 다른 모델과 일대 다 또는 다 대다 관계로 관련됩니다. 합니다 (내가 내 이전 코멘트에서 언급 된 표는 다 대다 관계의 경우에있을 것입니다 가입)
토비 1 케노비

@ Toby1Kenobi에게 감사드립니다. 열 배열 방법이 이미지 버전을 어떻게 설명하는지 궁금합니다 (어떻게 할 수 있는지 모르겠습니다). 당신의 전략은 가능합니다.
chaostheory

나는 레일 5.XX로 Carrierwave의이 기능을 구현 한 github.com/carrierwaveuploader/carrierwave/blob/master/... 하지만 성공적으로 실행할 수 아니다, 그것이 생성 오류입니다, UndefinedConversionError ("\x89" from ASCII-8BIT to UTF-8) SSR 솔루션의 경우, 함께 잘 작동 Rails 4.xx이지만 난관에 직면하고 있습니다 (Rails 5.xx 사용). 즉 ActionDispatch::Http::UploadedFile파일 이름 대신 데이터베이스에 저장하는 것 입니다. 또한 업 로더의 지정된 경로에 대해 공용 폴더에 파일을 저장하지 않습니다.
Mansi Shah

8

몇 가지 사소한 추가 SSR 답변에 대한 사항 :

accepts_nested_attributes_for 는 부모 개체의 컨트롤러를 변경할 필요가 없습니다. 따라서 수정하려면

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 / posts / _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 form not show up, nested form을 참조하세요.

Rails 5를 사용 하는 경우 accepts_nested_attributes_for 내부의 버그로 인해 Rails.application.config.active_record.belongs_to_required_by_defaulttruefalse(config / initializers / new_framework_defaults.rb에서)로 변경하십시오 (그렇지 않으면 accepts_nested_attributes_for 는 일반적으로 Rails 5에서 작동하지 않습니다).

편집 1 :

파괴 에 대해 추가하려면 :

models / post.rb에서

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

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?' } %>

      <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너무 놀라운 이유 입니다.


그것들은 실제로 @SSR 답변에 대한 주요 추가 사항이며 사소한 것이 아닙니다. :) accept_nested_attributes_for는 상당히 중요합니다. 실제로 자식 컨트롤러가 전혀 필요하지 않습니다. 귀하의 접근 방식을 따르면 업로드에 문제가 발생할 때 자녀에게 양식 오류 메시지를 표시하는 것뿐입니다.
Luis Fernando Alen

귀하의 의견에 감사드립니다. 업로드가 작동하지만 views / posts / _form.html.erb의 post_attachments 양식 필드에 추가 속성을 어떻게 추가 할 수 있는지 궁금합니다. <%= d.text_field :copyright, name: "album[diapos_attributes][][copyright]", class: 'form-field' %>모든 레코드가 아닌 마지막 레코드에만 저작권을 기록합니다.
SEJU

6

또한 여러 파일 업로드를 업데이트하는 방법을 알아 냈고 조금 리팩토링했습니다. 이 코드는 내 코드이지만 드리프트를 얻습니다.

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
코드를 공유해 주셔서 감사합니다. 시간이되면 내 gihub 저장소에서 코드를 업데이트하고 모든 사람이 코드를 쉽게 이해할 수 있도록 각 메소드에 대해 주석을다는 것을 잊지 마십시오.
SSR

1
리포지토리를 복제했습니다. PR을 수행 할 수있는 권한을 주실 수 있습니까?
Chris Habgood 2015

물론이지. 당신의 GitHub의 사용자 이름이란
SSR

내게 액세스 권한을 부여 할 기회가 있었습니까?
Chris Habgood jul.

2

다음은 모델에 대한 두 번째 리팩터링입니다.

  1. 개인 메서드를 모델로 이동합니다.
  2. @motherboard를 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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.