Rails Paperclip和多文件上传

15

我正在寻找一种解决方案,使用户能够通过一个file_field上传多个图像。我已经研究了诸如JQuery文件上传和Uploadify之类的选项,但是还没有找到具有可行解决方案的好例子。

我已经设置了多个图像,

 has_attached_file :asset,
                   :styles => { :large => "640x480", :medium => "300x300", :thumb => "100x100" },
                   :storage => :s3,
                   :s3_credentials => "#{Rails.root}/config/s3.yml",
                   :path => "/:contributor_id/:listing_name/:filename"

我现在正在显示5个独立的文件字段。

def new
  @listing = Listing.new
  5.times {@listing.assets.build }

  respond_to do |format|
    format.html # new.html.erb
    format.json { render json: @listing }
  end
end

我想要有

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

这允许用户在文件浏览器中选择多个文件。但是我如何使用嵌套模型处理它们?并使它们上传。

3个回答

35

这里有几个问题。

首先,Paperclip的has_attached_file方法不是一对多文件的关联。你似乎试图像处理Rails关联一样构建一个"资产(asset)"。Paperclip所做的只是在您的表中添加几个字段以存储有关文件的元数据,并且每次声明has_attached_file时都会附加一个文件。如果要附加5个文件,则需要执行类似以下操作:

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

或者,作为另一种选择,你可以创建另一个模型来存储文件。例如:

class Listing < ActiveRecord::Base
  has_many :assets
end

class Asset < ActiveRecord::Base
  belongs_to :listing
  has_attached_file :picture
end
那么,您可以将多个资产附加到一个列表中(您没有说明原始对象是什么,所以我只称其为“列表”)。
其次,在HTML中不存在多文件上传(因此,file_field方法不接受:multiple => true参数。如果您想要多文件上传,则必须使用超出Rails内置表单处理的内容。Uploadify是一个不错的选择(我以前用过)。有一个gem将文件字段转换为使用uploadify(并支持您想要的:multiple => true语法):https://github.com/mateomurphy/uploadify_rails3/wiki。但是,我无法担保它有多好。
我的建议是逐步开始。通过Flash上传到Rails可能是一个复杂的过程,涉及处理您表单中的CSRF元标记和其他字段。首先创建一个表单,允许用户上传一个文件并通过Paperclip存储它。然后,也许将has_attached_file声明拆分为另一个模型,以便您可以将1个或多个文件关联到模型(如上面的多模型代码块所示)。然后尝试添加Uploadify或其他可选项。Ernie Miller在集成Uploadify方面有一个不错的教程:http://erniemiller.org/2010/07/09/uploadify-and-rails-3/
首先要记住的是,has_attached_file只能附加一个文件。当您尝试调用@listing.assets时,不存在“assets”。有一个资产。如果您想要多个文件,请创建一个单独的模型并使用Rails的关联。

我已经创建了另一个名为“Listing”的模型。一个Listing有多个assets。我已经可以上传多个资产,只是不能通过单个“选择文件”字段上传。如果有人有使用uploadify的好例子,那就太好了。我不确定如何编写后台JS来处理每个图像并正确地将它们POST到服务器。 - kcollignon
3
您可以在file_field中添加:multiple => "multiple",这将启用从一个HTML文件输入字段中选择多个文件(至少在Chrome中是如此)。我还没有解决管理这些上传的问题,但我知道提交参数包括您选择和上传的多个文件数组。 - Victor S
1
你还需要在 form_for 中添加 :html => {:multipart => :true } - Victor S
只是补充一下:我会将文件上传移动到ajax中,这样您就可以从用户那里收集文件列表,然后在后台逐个上传它们:这样,即使在幕后不是这样的情况下,对于用户来说,它们似乎已经上传了很多文件。您还可以设置任何其他参数,以便在服务器上组织生成记录。有关使用ajax(jquery)进行文件上传的详细信息(可能有点棘手),请参见http://abandon.ie/notebook/simple-file-uploads-using-jquery-ajax - Max Williams

8

被接受的答案说,在HTML中没有多文件上传的功能。

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

这样可以让您选择多个图像并将它们作为数组发送。

如果您有关系Dog has_many ImagesImage has_attachment :file,请执行以下操作以一次性上传多个图像:

在您的html.erb文件中:

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

在你的 Dog 模型中
def files=(array = [])
  array.each do |f|
    images.create file: f
  end
end

假定您已经能够上传一张图片,但想要升级到同时上传多张图片。请注意,等待时间将增加。
为了帮助减少等待时间,请查看我关于快速上传的这个问题的帖子

如果可以的话,我会给这个点赞很多次。我卡了整整一天,第二天早上发现了这个东西。开启美好一天的绝佳方式!! - Int'l Man Of Coding Mystery

3
这里是多文件上传的完整示例。一个用户拥有许多上传,每个上传模型都有一个代表文件附件的头像。最终,我们在创建用户时会创建许多上传。 数据模型
#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

表单:

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

控制器

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接