How can I set paperclip's storage mechanism based on the current Rails environment?

后端 未结 7 1358
野性不改
野性不改 2020-12-12 11:07

I have a rails application that has multiple models with paperclip attachments that are all uploaded to S3. This app also has a large test suite that is run quite often. Th

相关标签:
7条回答
  • 2020-12-12 11:21

    After playing around with it for a while, I came up with a module that does what I want.

    Inside 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
    

    (Note: I'm using some custom paperclip interpolations above, like :uuid_partition, :uuid and :s3_authenticated_url. You'll need to modify things as needed for your particular application)

    Now, for every model that has paperclip attachments, you just have to include this shared module, and call the has_attachment method (instead of paperclip's has_attached_file)

    An example model file: app/models/user.rb:

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

    With this in place, you'll have files saved to the following locations, depending on your environment:

    Development:

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

    Test:

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

    Production:

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

    This does exactly what I'm looking for, hopefully it'll prove useful to someone else too. :)

    -John

    0 讨论(0)
  • 2020-12-12 11:24

    I like Barry's suggestion better and there's nothing keeping you from setting the variable to a hash, that can then be merged with the paperclip options.

    In config/environments/development.rb and test.rb set something like

    PAPERCLIP_STORAGE_OPTIONS = {}
    

    And in config/environments/production.rb

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

    Finally in your paperclip model:

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

    Update: A similar approach was recently implemented in Paperclip for Rails 3.x apps. Environment specific settings can now be set with config.paperclip_defaults = {:storage => :s3, ...}.

    0 讨论(0)
  • 2020-12-12 11:28

    How about this:

    1. Defaults are established in application.rb. The default storage of :filesystem is used, but the configuration for s3 is initialized
    2. Production.rb enables :s3 storage and changes the default path

    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 (uncomment this to try with s3 in development mode)

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

    Production.rb:

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

    In your model:

    has_attached_file :avatar 
    
    0 讨论(0)
  • 2020-12-12 11:34

    You can set global default configuration data in the environment-specific configuration files. For example, in 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']
      }
    })
    
    0 讨论(0)
  • 2020-12-12 11:34

    My solution is same with @runesoerensen answer:

    I create a module PaperclipStorageOption in config/initializers/paperclip_storage_option.rb The code is very simple:

    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
    

    and use it in our model has_attached_file :avatar, { :styles => { :medium => "1200x800>" } }.merge(PaperclipStorageOption.options)

    Just it, hope this help

    0 讨论(0)
  • 2020-12-12 11:34

    Use the :rails_env interpolation when you define the attachment path:

    has_attached_file :attachment, :path => ":rails_root/storage/:rails_env/attachments/:id/:style/:basename.:extension"
    
    0 讨论(0)
提交回复
热议问题