我正在我的Rails 3示例应用程序上使用CarrierWave。我想验证远程位置上传,以便在用户提交无效 URL 时不会出现标准错误异常,无论是空白还是不是图像:
CarrierWave::DownloadError in ImageController#create
trying to download a file which is not served over HTTP
这是我的模型:
class Painting < ActiveRecord::Base
attr_accessible :gallery_id, :name, :image, :remote_image_url
belongs_to :gallery
mount_uploader :image, ImageUploader
validates :name, :presence => true,
:length => { :minimum => 5, :maximum => 100 }
validates :image, :presence => true
end
这是我的控制器:
class PaintingsController < ApplicationController
def new
@painting = Painting.new(:gallery_id => params[:gallery_id])
end
def create
@painting = Painting.new(params[:painting])
if @painting.save
flash[:notice] = "Successfully created painting."
redirect_to @painting.gallery
else
render :action => 'new'
end
end
def edit
@painting = Painting.find(params[:id])
end
def update
@painting = Painting.find(params[:id])
if @painting.update_attributes(params[:painting])
flash[:notice] = "Successfully updated painting."
redirect_to @painting.gallery
else
render :action => 'edit'
end
end
def destroy
@painting = Painting.find(params[:id])
@painting.destroy
flash[:notice] = "Successfully destroyed painting."
redirect_to @painting.gallery
end
end
我真的不确定如何解决这个问题,所以任何见解都会很棒。
我遇到了同样的问题。不幸的是,看起来这是CarrierWave的设计缺陷......它不允许正确验证远程 URL。设置属性后,CarrierWave 将尝试立即下载资源,如果 url 无效、无法访问或资源没有预期类型,则会引发异常。DownloadError 或 IntegrityErrors 总是在任何验证发生之前抛出。
因此,我找不到使用其他验证器的好解决方法。我的解决方案最终看起来像这样:
valid = false
begin
par = params[:image].except(:remote_upload_url)
@image = Image.new(par)
# this may fail:
@image.remote_upload_url = params[:image][:remote_upload_url]
valid = true
rescue CarrierWave::DownloadError
@image.errors.add(:remote_upload_url, "This url doesn't appear to be valid")
rescue CarrierWave::IntegrityError
@image.errors.add(:remote_upload_url, "This url does not appear to point to a valid image")
end
# validate and save if no exceptions were thrown above
if valid && @image.save
redirect_to(images_configure_path)
else
render :action => 'new'
end
基本上,我将构造函数包装在救援块中,并最初设置除远程 url 之外的所有参数。当我设置它时,可能会出现异常,我通过在模型中手动设置错误来处理该异常。请注意,在此方案中不执行其他验证。这是一个黑客,但对我有用。
我希望可以在将来的版本中解决此问题,方法是将资源的下载延迟到模型验证阶段或之后。
这是一个非常烦人的问题。我现在在application_controller.rb
中做了rescue_from
,只是闪烁说明问题的消息。这是我能想到的最好的。我不喜欢堵塞控制器,如果您有多个模型需要这些验证,则必须使用该重复的代码。
rescue_from CarrierWave::DownloadError, :with => :carrierwave_download_error
rescue_from CarrierWave::IntegrityError, :with => :carrierwave_integrity_error
def carrierwave_download_error
flash[:error] = "There was an error trying to download that remote file for upload. Please try again or download to your computer first."
redirect_to :back
end
def carrierwave_integrity_error
flash[:error] = "There was an error with that remote file for upload. It seems it's not a valid file."
redirect_to :back
end
这个问题的解决方案已经添加到Github上的CarrierWave Wiki中。
编辑:
我现在正在尝试实施建议的解决方案,但我无法让它工作。我正在使用 AR on Rails 3.1.3。
按照 wiki 上的方式实现代码会导致验证实际上发生得很好。当我尝试上传胡言乱语时,我会收到一条很好的验证消息。问题是正常上传也被阻止了。
CarrierWave wiki 上的解决方案对我不起作用。正如Peter Hulst所提到的,CarrierWave在验证之前加载文件。我找到了一种方法,方法是在抛出异常时捕获异常,并在以后将其添加回验证错误。出于某种原因,当引发异常时,所有其他记录的属性将变为 nil,因此还必须在验证之前捕获并重新添加它们。这些代码全部包含在您的模型中。
这仍然需要一些返工才能使用来自配置的错误消息,而不是硬编码。
attr_accessor :additional_error_message, :original_attributes
def initialize(*args)
self.original_attributes = args[0]
begin
super
rescue CarrierWave::IntegrityError # bad file type
self.additional_error_message = 'must be a PNG, JPEG, or GIF file' # depends on your whitelist
rescue OpenURI::HTTPError # 404
self.additional_error_message = 'could not be found'
rescue RuntimeError # redirection
self.additional_error_message = 'could not be loaded'
rescue CarrierWave::DownloadError
self.additional_error_message = 'could not be loaded'
rescue
self.additional_error_message = 'could not be loaded'
end
end
before_validation do |image|
if additional_error_message.present?
errors.add(remote_image_url, additional_error_message)
self.name = original_attributes[:name] # replace this with re-adding all of your original attributes other than the remote_image_url
end
end
# the image will have an "is blank" error, this removes that
after_validation do |image|
errors.delete(:image) if additional_error_message.present?
end