5

我在我的 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

我不确定如何解决这个问题,所以任何见解都会很棒。

4

4 回答 4

8

我遇到了同样的问题。不幸的是,这看起来像是 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 之外的所有参数。当我设置它时,可能会发生我通过在模型中手动设置错误来处理的异常。请注意,在此方案中不执行其他验证。这是一个黑客,但对我有用。

我希望可以在未来的版本中解决这个问题,方法是将资源的下载延迟到模型验证阶段或之后。

于 2011-07-05T07:55:28.810 回答
1

这是一个非常烦人的问题。我现在做了rescue_fromapplication_controller.rb的,只是闪现说明问题的消息。这是我能想到的最好的了。如果您有多个模型需要这些验证,我不喜欢堵塞控制器并不得不使用重复的代码。

  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
于 2012-09-13T04:47:01.850 回答
0

此问题的解决方案已添加到 Github 上的CarrierWave Wiki

编辑:
我现在正在尝试实施建议的解决方案,但我无法让它工作。我在 Rails 3.1.3 上使用 AR。

以 wiki 上的方式实现代码会导致验证实际上进行得很好。当我尝试上传乱码时,我会收到一条很好的验证消息。问题是正常的上传也被阻止了。

于 2011-12-20T12:58:41.903 回答
0

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
于 2012-12-16T17:51:52.490 回答