由于validation错误而无法保存模型时,不会丢失回形针附件

该场景是一个普通模型,其中包含回形针附件以及具有各种validation的其他一些列。 当由于与附件无关的validation错误而无法保存要创建对象的表单时,会保留字符串等列并为用户保留预填充,但选择上载的文件完全丢失且必须由用户重新选择。

在模型validation错误的情况下,是否有标准方法来保留附件? 这似乎是一个非常常见的用例。

在没有所有者的情况下保存文件然后在成功保存之后重新连接到对象的解决方案似乎是不优雅的,所以我希望避免这种情况。

切换到使用CarrierWave。 我知道这是在评论中,但我只是整天都在进行过渡,所以我的回答可能仍然有用。

首先,您可以按照关于设置载波的优秀轨道广播: http ://railscasts.com/episodes/253-carrierwave-file-uploads

要使其保留post之间的图像,您需要添加带有后缀“缓存”的隐藏字段:

<%= form_for @user, :html => {:multipart => true} do |f| %> 

<%= f.file_field :avatar %> <%= f.hidden_field :avatar_cache %>

<% end %>

对于Heroku

如果您像我一样部署到Heroku,则需要进行一些更改才能使其正常工作,因为缓存通过暂时将上载保存在名为public / uploads的目录中来实现。 由于文件系统只是在Heroku中读取,所以你需要让它使用tmp文件夹,并让机架从那里提供静态文件。

告诉carrierwave使用tmp文件夹进行缓存。

在你的config / initializers / carrierwave.rb中(如果不存在则随意创建),添加:

 CarrierWave.configure do |config| config.root = Rails.root.join('tmp') config.cache_dir = 'carrierwave' end 

配置机架以从tmp / carrierwave文件夹中提供静态文件

在您的config.ru文件中,添加:

 use Rack::Static, :urls => ['/carrierwave'], :root => 'tmp' 

有关全function准系统rails / carrierwave / s3 / heroku应用程序的示例,请查看:

https://github.com/trevorturk/carrierwave-heroku (没有隶属关系,只是有用)。

希望这可以帮助!

我不得不在最近使用PaperClip的项目中解决这个问题。 我试过在模型中使用after_validation和before_save调用cache_images(),但由于某些我无法确定的原因它在创建时失败,所以我只是从控制器调用它。

模型:

 class Shop < ActiveRecord::Base attr_accessor :logo_cache has_attached_file :logo def cache_images if logo.staged? if invalid? FileUtils.cp(logo.queued_for_write[:original].path, logo.path(:original)) @logo_cache = encrypt(logo.path(:original)) end else if @logo_cache.present? File.open(decrypt(@logo_cache)) {|f| assign_attributes(logo: f)} end end end private def decrypt(data) return '' unless data.present? cipher = build_cipher(:decrypt, 'mypassword') cipher.update(Base64.urlsafe_decode64(data).unpack('m')[0]) + cipher.final end def encrypt(data) return '' unless data.present? cipher = build_cipher(:encrypt, 'mypassword') Base64.urlsafe_encode64([cipher.update(data) + cipher.final].pack('m')) end def build_cipher(type, password) cipher = OpenSSL::Cipher::Cipher.new('DES-EDE3-CBC').send(type) cipher.pkcs5_keyivgen(password) cipher end end 

控制器:

 def create @shop = Shop.new(shop_params) @shop.user = current_user @shop.cache_images if @shop.save redirect_to account_path, notice: 'Shop created!' else render :new end end def update @shop = current_user.shop @shop.assign_attributes(shop_params) @shop.cache_images if @shop.save redirect_to account_path, notice: 'Shop updated.' else render :edit end end 

视图:

 = f.file_field :logo = f.hidden_field :logo_cache - if @shop.logo.file? %img{src: @shop.logo.url, alt: ''} 

按照@galatians的想法,我得到了这个解决方案(并且很好地工作)

为该示例创建了一个回购:* https://github.com/mariohmol/paperclip-keeponvalidation

  1. 首先要做的是在基本活动记录中放置一些方法,因此每个使用附加的模型都可以使它工作

在config / initializers / active_record.rb中

 module ActiveRecord class Base def decrypt(data) return '' unless data.present? cipher = build_cipher(:decrypt, 'mypassword') cipher.update(Base64.urlsafe_decode64(data).unpack('m')[0]) + cipher.final end def encrypt(data) return '' unless data.present? cipher = build_cipher(:encrypt, 'mypassword') Base64.urlsafe_encode64([cipher.update(data) + cipher.final].pack('m')) end def build_cipher(type, password) cipher = OpenSSL::Cipher::Cipher.new('DES-EDE3-CBC').send(type) cipher.pkcs5_keyivgen(password) cipher end #ex: @avatar_cache = cache_files(avatar,@avatar_cache) def cache_files(avatar,avatar_cache) if avatar.queued_for_write[:original] FileUtils.cp(avatar.queued_for_write[:original].path, avatar.path(:original)) avatar_cache = encrypt(avatar.path(:original)) elsif avatar_cache.present? File.open(decrypt(avatar_cache)) {|f| assign_attributes(avatar: f)} end return avatar_cache end end end 
  1. 之后,在您的模型和附加字段中包含上面的代码

例如,我将其包含在/models/users.rb中

  has_attached_file :avatar, PaperclipUtils.config attr_accessor :avatar_cache def cache_images @avatar_cache=cache_files(avatar,@avatar_cache) end 
  1. 在你的控制器中,添加它以从缓存图像中获取(在保存模型的点之前)

    @ user.avatar_cache = params [:user] [:avatar_cache]

    @ user.cache_images

    @ user.save

  2. 最后在您的视图中包含此内容,以记录当前临时图像的位置

f.hidden_​​field:avatar_cache

  1. 如果要在视图中显示实际文件,请包含它:
 <% if @user.avatar.exists? %>  
<%= image_tag @user.avatar.url %>
<% end %>

截至2013年9月,paperclip无意在validation后“修复”附加文件的丢失。 “问题是(恕我直言)更容易,更正确地避免而不是解决”

https://github.com/thoughtbot/paperclip/issues/72#issuecomment-24072728

我正在考虑John Gibb早期解决方案中提出的CarrierWave解决方案

另请查看refile (更新的选项)

特点

  • 可配置的后端,文件系统,S3等……
  • 与ORM轻松集成
  • 即时操作图像和其他文件
  • 流式IO可实现快速且内存友好的上传
  • 在表单重新显示时工作,即validation失败时,即使在S3上也是如此
  • 轻松直接上传,甚至是S3
  • 支持多个文件上传

https://gorails.com/episodes/file-uploads-with-refile

如果不需要图像,为什么不将表单分成两个阶段,第一个创建对象,第二个页面允许您添加可选信息(如照片)。

或者,您可以在用户输入信息时validation表单,这样您就不必提交表单以查明您的数据无效。

首先保存你的照片,而不是尝试其余的

假设你有一个使用回形针头像的用户:

 def update @user = current_user unless params[:user][:avatar].nil? @user.update_attributes(avatar: params[:user][:avatar]) params[:user].delete :avatar end if @user.update_attributes(params[:user]) redirect_to edit_profile_path, notice: 'User was successfully updated.' else render action: "edit" end end 

在视图文件中只放入if条件,只接受有效id的记录。 在我的场景中,这是代码片段

  

Uploaded files:

    <% @user.org.crew.w9_files.each do |file| %> <% if file.id.present? %>
  • <%= rails code to display value %>
  • <% end %> <% end %>