该场景是一个普通模型,其中包含回形针附件以及其他一些具有各种验证的列。当由于与附件无关的验证错误而无法保存要创建对象的表单时,会保留字符串等列并为用户保留预填充,但选择用于上载的文件完全丢失且必须由用户重新选择。
在模型验证错误的情况下,是否存在保留附件的标准方法?这似乎是一个非常常见的用例。
在没有所有者的情况下保存文件然后在成功保存之后重新连接到对象的解决方案似乎是不优雅的,所以我希望避免这种情况。
答案 0 :(得分:12)
切换到使用CarrierWave。我知道这是在评论中,但我只是整天都在进行过渡,所以我的答案可能仍然有用。
首先,您可以按照关于设置载波的精彩轨道广播:http://railscasts.com/episodes/253-carrierwave-file-uploads
要使其保留帖子之间的图像,您需要添加带有后缀“缓存”的隐藏字段:
<%= form_for @user, :html => {:multipart => true} do |f| %>
<p>
<label>My Avatar</label>
<%= f.file_field :avatar %>
<%= f.hidden_field :avatar_cache %>
</p>
<% end %>
如果您像我一样部署到Heroku,则需要进行一些更改才能使其正常工作,因为缓存的工作方式是将上传暂时保存在名为public / uploads的目录中。由于文件系统只是Heroku中的readonly,你需要让它使用tmp文件夹,并让机架从那里提供静态文件。
在你的config / initializers / carrierwave.rb中(如果不存在则随意创建),添加:
CarrierWave.configure do |config|
config.root = Rails.root.join('tmp')
config.cache_dir = 'carrierwave'
end
在config.ru文件中,添加:
use Rack::Static, :urls => ['/carrierwave'], :root => 'tmp'
有关全功能准系统rails / carrierwave / s3 / heroku应用程序的示例,请查看:
https://github.com/trevorturk/carrierwave-heroku(没有任何从属关系,只是有用)。
希望这有帮助!
答案 1 :(得分:3)
我必须在最近使用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: ''}
答案 2 :(得分:3)
遵循@galatians的想法,我得到了这个解决方案(并且工作得非常漂亮)
为该示例创建了一个repo: * https://github.com/mariohmol/paperclip-keeponvalidation
在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
例如,我将其包含在/models/users.rb
中 has_attached_file :avatar, PaperclipUtils.config
attr_accessor :avatar_cache
def cache_images
@avatar_cache=cache_files(avatar,@avatar_cache)
end
在您的控制器中,添加此项以从缓存图像中获取(在保存模型的位置之前)
@ user.avatar_cache = params [:user] [:avatar_cache]
@ user.cache_images
@ user.save
最后在您的视图中包含此内容,以记录当前临时图像的位置
f.hidden_field:avatar_cache
<% if @user.avatar.exists? %> <label class="field">Actual Image </label> <div class="field file-field"> <%= image_tag @user.avatar.url %> </div> <% end %>
答案 3 :(得分:1)
截至2013年9月,paperclip无意在验证后“修复”附加文件的丢失。 “问题是(恕我直言)比解决问题更容易和更正确地避免”
https://github.com/thoughtbot/paperclip/issues/72#issuecomment-24072728
我正在考虑John Gibb早期解决方案中提出的CarrierWave解决方案
答案 4 :(得分:1)
同时查看refile(更新选项)
功能强>:
答案 5 :(得分:0)
如果不需要图像,为什么不将表单分成两个阶段,第一个创建对象,第二个页面允许您添加可选信息(如照片)。
或者,您可以在用户输入信息时验证表单,这样您就不必提交表单以查明您的数据无效。
答案 6 :(得分:0)
首先保存你的照片而不是尝试其余的
假设您的用户使用了回形针头像:
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
答案 7 :(得分:0)
在视图文件中只放入if条件,只接受有效id的记录。 在我的场景中,这是代码片段
<p>Uploaded files:</p>
<ul>
<% @user.org.crew.w9_files.each do |file| %>
<% if file.id.present? %>
<li> <%= rails code to display value %> </li>
<% end %>
<% end %>
</ul>