如何根据当前的Rails环境设置Paperclip的存储机制?

75
我有一个Rails应用程序,其中包含多个带有Paperclip附件的模型,所有这些模型都上传到S3。该应用程序还有一个大型测试套件,经常运行。缺点是每次测试运行时都会上传大量文件到我们的S3帐户,使测试套件运行缓慢。它也稍微减慢了开发速度,并且需要您连接互联网才能在代码上工作。
有没有一种合理的方法根据Rails环境设置paperclip存储机制?理想情况下,我们的测试和开发环境将使用本地文件系统存储,而生产环境将使用S3存储。
我还想将此逻辑提取到某种共享模块中,因为我们有几个模型需要此行为。我想避免像每个模型内部这样的解决方案:
### We don't want to do this in our models...
if Rails.env.production?
  has_attached_file :image, :styles => {...},
                    :path => "images/:uuid_partition/:uuid/:style.:extension",
                    :storage => :s3,
                    :url => ':s3_authenticated_url', # generates an expiring url
                    :s3_credentials => File.join(Rails.root, 'config', 's3.yml'),
                    :s3_permissions => 'private',
                    :s3_protocol => 'https'
else
  has_attached_file :image, :styles => {...},
                    :storage => :filesystem
                    # Default :path and :url should be used for dev/test envs.
end

更新: 粘性部分是附件的:path:url选项需要根据使用的存储系统而异。

非常感谢您的任何建议或建议! :-)

7个回答

78

我更喜欢Barry的建议,你可以将变量设置为散列表,然后将其与paperclip选项合并,没有任何限制。

在config/environments/development.rb和test.rb中设置类似的内容。

PAPERCLIP_STORAGE_OPTIONS = {}

在 config/environments/production.rb 文件中

PAPERCLIP_STORAGE_OPTIONS = {:storage => :s3, 
                               :s3_credentials => "#{Rails.root}/config/s3.yml",
                               :path => "/:style/:filename"}

最后在您的 paperclip 模型中:

has_attached_file :image, {
    :styles => {:thumb => '50x50#', :original => '800x800>'}
}.merge(PAPERCLIP_STORAGE_OPTIONS)

更新:最近在Rails 3.x应用中实现了类似的方法,在Paperclip中。 环境特定设置现在可以使用config.paperclip_defaults = {:storage => :s3, ...}来设置。


32

您可以在环境特定的配置文件中设置全局默认配置数据。例如,在config/environments/production.rb中:

Paperclip::Attachment.default_options.merge!({
  :storage => :s3,
  :bucket => 'wheresmahbucket',
  :s3_credentials => {
    :access_key_id => ENV['S3_ACCESS_KEY_ID'],
    :secret_access_key => ENV['S3_SECRET_ACCESS_KEY']
  }
})

更少的元数据,更明确的内容,这绝对是正确的做法。这甚至可以在每个环境中提取到一个YAML文件中,并且拥有自己的命名空间。谢谢@austinfromboston。 - Kenneth Kalmer

27

经过一段时间的试验和尝试,我编写了一个模块满足我的需求。

app/models/shared/attachment_helper.rb中:

module Shared
  module AttachmentHelper

    def self.included(base)
      base.extend ClassMethods
    end

    module ClassMethods
      def has_attachment(name, options = {})

        # generates a string containing the singular model name and the pluralized attachment name.
        # Examples: "user_avatars" or "asset_uploads" or "message_previews"
        attachment_owner    = self.table_name.singularize
        attachment_folder   = "#{attachment_owner}_#{name.to_s.pluralize}"

        # we want to create a path for the upload that looks like:
        # message_previews/00/11/22/001122deadbeef/thumbnail.png
        attachment_path     = "#{attachment_folder}/:uuid_partition/:uuid/:style.:extension"

        if Rails.env.production?
          options[:path]            ||= attachment_path
          options[:storage]         ||= :s3
          options[:url]             ||= ':s3_authenticated_url'
          options[:s3_credentials]  ||= File.join(Rails.root, 'config', 's3.yml')
          options[:s3_permissions]  ||= 'private'
          options[:s3_protocol]     ||= 'https'
        else
          # For local Dev/Test envs, use the default filesystem, but separate the environments
          # into different folders, so you can delete test files without breaking dev files.
          options[:path]  ||= ":rails_root/public/system/attachments/#{Rails.env}/#{attachment_path}"
          options[:url]   ||= "/system/attachments/#{Rails.env}/#{attachment_path}"
        end

        # pass things off to paperclip.
        has_attached_file name, options
      end
    end
  end
end

(注:上面我使用了一些自定义的paperclip插值,例如:uuid_partition:uuid:s3_authenticated_url。您需要根据您的特定应用程序进行必要的修改)

现在,对于每个具有paperclip附件的模型,您只需包含此共享模块,并调用has_attachment方法(而不是paperclip的has_attached_file

一个示例模型文件:app/models/user.rb

class User < ActiveRecord::Base
  include Shared::AttachmentHelper  
  has_attachment :avatar, :styles => { :thumbnail => "100x100>" }
end

当这个被放置好,你将会有文件存在以下位置,根据你的环境而定:

开发环境:

RAILS_ROOT + public/attachments/development/user_avatars/aa/bb/cc/aabbccddeeff/thumbnail.jpg

测试环境:

RAILS_ROOT + public/attachments/test/user_avatars/aa/bb/cc/aabbccddeeff/thumbnail.jpg

生产环境:

https://s3.amazonaws.com/your-bucket-name/user_avatars/aa/bb/cc/aabbccddeeff/thumbnail.jpg

这正是我在寻找的,希望对其他人也有所帮助。 :)

-John


干得好。是的,需要比我提供的更多的抽象! :) - Barry Hess
我之前在使用上面提到的常数/哈希方法时遇到了麻烦,但这个方法很好用,而且我喜欢它让我可以在一个地方统一管理所有的Paperclip逻辑。谢谢! - neezer

5
这样如何:
  1. 默认值在application.rb文件中设置。使用默认的“:filesystem”存储,但是对于s3的配置进行了初始化
  2. Production.rb启用“:s3”存储并更改默认路径

Application.rb

config.paperclip_defaults = 
{
  :hash_secret => "LongSecretString",
  :s3_protocol => "https",
  :s3_credentials => "#{Rails.root}/config/aws_config.yml",
  :styles => { 
    :original => "1024x1024>",
    :large => "600x600>", 
    :medium => "300x300>",
    :thumb => "100x100>" 
  }
}

Development.rb(取消注释以尝试在开发模式下使用s3)

# config.paperclip_defaults.merge!({
#   :storage => :s3,
#   :bucket => "mydevelopmentbucket",
#   :path => ":hash.:extension"
# })

Production.rb:

config.paperclip_defaults.merge!({
  :storage => :s3,
  :bucket => "myproductionbucket",
  :path => ":hash.:extension"
})

在你的模型中:
has_attached_file :avatar 

2

你不能在production/test/development.rb中设置环境变量吗?

PAPERCLIP_STORAGE_MECHANISM = :s3

然后:

has_attached_file :image, :styles => {...},
                  :storage => PAPERCLIP_STORAGE_MECHANISM,
                  # ...etc...

1
嘿,巴里,这是一个好建议,但是“...等等...”中的各种选项会引起麻烦。我发现:path和:url选项需要根据是使用:s3还是:filesystem存储而有所不同。我将用更好的示例更新问题。谢谢,--约翰 - John Reilly

0

我的解决方案与@runesoerensen的答案相同:

我在config/initializers/paperclip_storage_option.rb中创建了一个名为PaperclipStorageOption的模块。代码非常简单:

module PaperclipStorageOption
  module ClassMethods
    def options
      Rails.env.production? ? production_options : default_options
    end

    private

    def production_options
      {
        storage: :dropbox,
        dropbox_credentials: Rails.root.join("config/dropbox.yml")
      }
    end

    def default_options
      {}
    end
  end

  extend ClassMethods
end

在我们的模型中使用它 has_attached_file :avatar, { :styles => { :medium => "1200x800>" } }.merge(PaperclipStorageOption.options)

就这样,希望能有所帮助


-4
使用 :rails_env 插值符号来定义附件路径:
has_attached_file :attachment, :path => ":rails_root/storage/:rails_env/attachments/:id/:style/:basename.:extension"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接