ruby-on-rails-3 – Rails 3.2 HTML5多文件上传,Carrierwave,没
目前使用Rails 3.2和Carrierwave.
我有多个文件设置,但它需要多个文件字段,但我只需要一个文件字段.如果浏览器不支持HTML5 multiple属性,我将提供此默认值. 调节器 def new @ad = Ad.new 5.times { @ad.images.build } // provides multiple file fields in the view. end def create ad = Ad.new(params[:ad]) user = User.find(session[:user_id]) if user.ads << ad flash[:notice] = "Ad successfully saved." redirect_to ad_listing_path(ad.id,ad.slug) else render :new,:alert => "Ad was not saved." end end 视图 <%= f.fields_for :images do |a| %> <% if a.object.new_record? %> <%= a.file_field :image,:multiple => true %><br> <% end %> <% end %> 如果5次{@ ad.images.build}提供我的多个字段,那么显示1个接受多个字段的文件字段的正确方法是什么? 解决方法
这似乎是一个没有好答案的热门问题,所以我将在这里完全回答.在开始之前,我将提到代码在
https://github.com/mdchaney/multi处可用,但请继续查看如何以最简单的方式执行此操作.
在我们去那里之前,在HTML 5中,文件输入字段可以具有“多个”属性集.如果已设置,则结果与具有相同名称的多个文件输入相同.在Rails中,为表单构建器构建的文件输入字段设置“multiple:true”将使其作为文件数组上载. <%= f.file_field :files,:multiple => true %> 变 <input id="model_files" multiple="multiple" name="model[files][]" type="file" /> 其中“model”是模型的名称.此输入控件将(至少在Chrome中)具有“选择文件”标签而不是“选择文件”. CarrierWave本身不能处理这个问题.它使用单个文本字段来存储有关单个文件的信息,以及一些内部逻辑来确定存储该文件(及其衍生物)的位置.有可能破解它将多个文件的信息放在单个文本字段中,选择具有set分隔符的编码.这需要在CarrierWave上进行大量的工作和黑客攻击. 但是,我并不关心破解CarrierWave,所以问题变成了这样一个事实,即将多个文件附加到一个项目实际上是一对多的关系,或者在Rails术语中是“has_many”.因此,可以使用简单的属性编写器将文件输入字段中的文件添加到多个附加记录中. 有了这个,我提出了最简单的方法来使用带有多个属性集的HTML 5文件输入字段.有很多方法可以用jQuery和flash做到这一点,但我现在用它来展示如何用直接的HTML 5来实现它. 在我们的示例中,我们将有一个简单的“上传”模型,每个模型都有一个名称和任意数量的链接文件,这些文件将存储在另一个名为linked_files的模型中(使其变得简单,对吧?). linked_file将保存原始文件名,提供内容类型,当然还有CarrierWave存储其信息的字段. 让我们为上传创建脚手架,然后创建linked_files的模型: rails g scaffold Upload name:string rails g model LinkedFile upload:references filename:string mime_type:string file:string 完成后,如果我们希望在字段上添加“非null”约束,我们可以设置限制: class CreateUploads < ActiveRecord::Migration def change create_table :uploads do |t| t.string :name,limit: 100,null: false t.timestamps end end end class CreateLinkedFiles < ActiveRecord::Migration def change create_table :linked_files do |t| t.references :upload,null: false t.string :filename,limit: 255,null: false t.string :mime_type,null: false t.string :file,null: false t.timestamps end add_index :linked_files,:upload_id end end 现在,让我们通过添加一个名为“files”的新属性writer来修复Upload模型: class Upload < ActiveRecord::Base has_many :linked_files,inverse_of: :upload,dependent: :destroy accepts_nested_attributes_for :linked_files,reject_if: :all_blank,allow_destroy: true validates_associated :linked_files attr_accessible :name,:files,:linked_files_attributes def files=(raw_files) raw_files.each do |raw_file| self.linked_files.build({filename: raw_file.original_filename,mime_type: raw_file.content_type,file: raw_file}) end end validates :name,presence: true,length: { maximum: 100 } end 其中大部分是Rails模型的正常声明.这里唯一真正的补充是“files =”方法,它将一组上传的文件放在一个数组中,并为每个文件创建一个“linked_file”. 我们需要一个CarrierWave上传器: class FileUploader < CarrierWave::Uploader::Base storage :file def store_dir "uploads/#{model.class.to_s.underscore}/#{model.id}/#{mounted_as}" end end 这是最简单的上传器,您可能希望限制上传的文件类型或其他.现在,LinkedFile模型: class LinkedFile < ActiveRecord::Base mount_uploader :file,FileUploader belongs_to :upload,inverse_of: :linked_files attr_accessible :file,:filename,:mime_type,:file_cache,:remove_file validates :filename,length: { maximum: 255 } validates :mime_type,length: { maximum: 255 } end 这没什么特别的,只添加了:file_cache和:remove_file作为文件上传器的可访问属性. 除了观点之外,我们现在已经完成了.我们实际上只需要更改表单,但我们也会更改“show”以允许访问上传的文件.这是_form.html.erb文件: <%= form_for(@upload,{ multipart: true }) do |f| %> <% if @upload.errors.any? %> <div id="error_explanation"> <h2><%= pluralize(@upload.errors.count,"error") %> prohibited this upload from being saved:</h2> <ul> <% @upload.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %> <div class="field"> <%= f.label :name %><br /> <%= f.text_field :name %> </div> <% if f.object.linked_files.size == 0 -%> <div class="field"> <%= f.label :files %><br /> <%= f.file_field :files,:multiple => true %> </div> <% else -%> <fieldset> <legend>Linked Files</legend> <%= f.fields_for :linked_files do |lff| -%> <div class="field"> <%= lff.label :filename %><br /> <%= lff.text_field :filename %> </div> <div class="field"> <%= lff.label :mime_type %><br /> <%= lff.text_field :mime_type %> </div> <div class="field"> <%= lff.label :file,'File (replaces current selection)' %><br /> <%= lff.file_field :file %> <%= lff.hidden_field :file_cache %> </div> <div class="field"> <%= lff.check_box :_destroy %> Remove this file </div> <hr /> <% end -%> </fieldset> <% end -%> <div class="actions"> <%= f.submit %> </div> <% end %> 我添加了两段代码.如果@upload对象没有与之关联的“linked_files”,我只显示多个文件输入.否则,我会向每个linked_file显示其所有信息.可以向上传添加“文件”方法并以这种方式处理它,但这样做会在请求中丢失mime类型. 您可以轻松测试此内容,因为上传“名称”是必填字段.启动服务器并转到http://127.0.0.1:3000/uploads以查看该应用程序.单击“新建”链接,选择一些文件并点击“创建上传”而不提供名称.下一页将显示您正在等待的所有文件.添加名称时,所有内容都会保存.让我们修改“show”动作来显示linked_files: <p id="notice"><%= notice %></p> <p> <b>Name:</b> <%= @upload.name %> </p> <p> <b>Files:</b><br /> </p> <table> <thead><tr><th>Original Filename</th><th>Content Type</th><th>Link</th></tr></thead> <tbody> <% @upload.linked_files.each do |linked_file| -%> <tr> <td><%= linked_file.filename %></td> <td><%= linked_file.mime_type %></td> <td><%= link_to linked_file.file.url,linked_file.file.url %></td> </tr> <% end -%> </tbody> </table> <%= link_to 'Edit',edit_upload_path(@upload) %> | <%= link_to 'Back',uploads_path %> 在这里我只添加了一个“文件”标题和一个显示所有这些标题的表,并提供了一个查看链接.没什么好看的,但它确实有效. 如果我将它变成一个真正的应用程序,我也可能会提供一个文件列表或上传索引页面上最少的文件数. 就是这样了.再次,如果你想下载它,整个测试应用程序可以在github上获得,但我已经把我的整个Rails生成语句和更改发布在这篇文章中. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |