2

我正在使用带有雾 (1.18.0)、mini_magick (3.6.0) 和 Rackspace CloudFiles 的carrierwave (0.9.0)。我想合成图像。

我有两个模型:产品和电子邮件。在产品上传器中,我试图创建一个循环遍历所有电子邮件并将产品图像合成到每个email.background_image. 我还想控制此图像的名称,即:

"#{email.name}_#{product.product_number}.jpg"

我在下面包含了我的过程和方法。测试结果是一个文件被上传到名为“email_123.png”的云文件和原始图像。该名称目前不包含电子邮件的名称,并且图像未合成,我只收到了原件。我在这里遗漏了什么明显的东西吗?谢谢!

version :email do
  process :email_composite

  def email_composite
    Email.all.each do |email|
      email_image = email.secondary.url
      email_name = email.name
      merge(email_name, email_image)
    end
  end

  def merge(email_name, email_image)
    manipulate! do |img|
      @product = Product.find(model.id)
      email_image = ::MiniMagick::Image.open(email_image)

      img = email_image.composite(img, "jpg") do |c|
        c.gravity "Center"
      end
      # img = yield(img) if block_given?
      img = img.name "#{email_name}_#{@product.product_number}.png"

      img
    end
  end
end

更新

首先,我要感谢您的彻底回答,所涉及的努力是显而易见的,值得赞赏。

在尝试实施此解决方案时,我还有一些问题。在#compose 方法中,您有:

email_image = ::MiniMagick::Image.open(email_image)

这应该是:

email_image = ::MiniMagick::Image.open(email_image_url)

我通过看到下面包含的 define_method 调用来假设这一点(顺便说一句,这太棒了——我不知道你能做到这一点):

define_method(:email_image_url) { email.secondary.url }

在获得未定义的方法“versions_for”后,我切换了#versions_for 方法,因为我总是希望所有电子邮件都有一个辅助版本。

以前的版本

def self.versions_for emails
  reset_versions!
  email.find_each { |e| version_for_email(e) }
end

更改后的versions_for

def self.versions_for
  reset_versions!
  Email.all.find_each { |e| version_for_email(e) }
end

此更改使我摆脱了错误。

所有这一切的最终结果是一切看起来都好像可以正常工作,我可以毫无错误地上传图像,但现在我没有电子邮件版本的结果图像。我仍然得到我的其他版本,如拇指等。知道可能导致这种情况的原因吗?再次感谢您迄今为止提供的所有帮助。

4

1 回答 1

2

在同一版本中多次调用manipulate!会将其块中的更改应用于同一图像;您想要的是创建多个版本,每个版本对应一个Email. 这很棘手,因为 CarrierWave 确实希望您在代码中拥有一小组静态定义的版本;它实际上构建了一个新的匿名Uploader类来处理每个版本。

我们可以欺骗它动态构建版本,但它非常难看。此外,我们必须小心不要保留对过时上传器类的引用,否则我们将无休止地累积类并最终耗尽内存!

# in ProductUploader:

# CarrierWave stores its version classes in two places:
#
# 1. In a @versions hash, stored within the class; and
# 2. As a constant in your uploader, with a name based on its #object_id.
#
# We have to clean out both of them to be sure old versions get garbage
# collected!
def self.reset_versions!
  versions.keys.select { |k| k =~ /^email_/ }.each do |k|
    u = versions.delete(k)[:uploader]
    remove_const("Uploader#{u.object_id}".gsub('-', '_'))
  end
end

def self.version_name_for email
  "email_#{email.name}".to_sym
end

# Dynamically generate the +version+ that corresponds to the image composed
# with the image from +email+.
def self.version_for_email email
  version(version_name_for(email)) do
    process :compose

    # Use +define_method+ here so that +email+ in the block refers to the
    # argument "email" from the outer scope.
    define_method(:email_image_url) { email.secondary.url }

    # Use the same trick to override the filename for this version.
    define_method(:filename) { "#{email_name}_#{model.product_number}.png" }

    # Compose +email_image+ on top of the product image.
    def compose
      manipulate! do |img|
        email_image = ::MiniMagick::Image.open(email_image_url)

        # Do the actual composition.
        img = email_image.composite(img, "jpg") do |c|
          c.gravity "Center"
        end

        img
      end
    end
  end
end

# Clear out any preexisting versions and generate new ones based on the
# contents of +emails+.
def self.versions_for emails
  reset_versions!
  email.find_each { |e| version_for_email(e) }
end

# After you call Product.versions_for(emails), you can use this to fetch
# the version for a specific email:
def version_for_email email
  versions[self.class.version_name_for(email)]
end

要使用此功能,请务必调用Product.versions_for(Email.all)(可能在 a 中before_filter),然后,您可以使用以下命令访问特定电子邮件的版本@p.product.version_for(email)

# in your controller:

def action
  # Recreate a +version+ for each email.
  # If you don't want to overlay *every* email's image, you can also pass a
  # subset here.
  ProductUploader.versions_for(Email.all)

  # Upload a file and its versions to Cloud Files...
  @p = Product.find(params[:id])
  @p.product = File.open(Rails.root.join('clouds.jpg'), 'r')
  @p.save!
end

在您看来,您可以url像往常一样使用或任何其他助手:

# in a view:

<% Email.all.find_each do |email| %>
  <%= image_tag @p.product.version_for_email(email).url %>
<% end %>
于 2013-11-23T03:48:37.370 回答