Module: Paperclip::Storage::Database

Defined in:
lib/paperclip/storage.rb

Overview

Store files in a database.

Usage is identical to the file system storage version, except:

  1. In your model specify the “database” storage option; for example:

has_attached_file :avatar, :storage => :database
  1. The file will be stored in a column called [attachment name]_file (e.g. “avatar_file”) by default.

To specify a different column name, use :column, like this:

has_attached_file :avatar, :storage => :database, :column => 'avatar_data'

If you have defined different styles, these files will be stored in additional columns called [attachment name]_[style name]_file (e.g. “avatar_thumb_file”) by default.

To specify different column names for styles, use :column in the style definition, like this:

has_attached_file :avatar,
                  :storage => :database,
                  :styles => { 
                    :medium => {:geometry => "300x300>", :column => 'medium_file'},
                    :thumb => {:geometry => "100x100>", :column => 'thumb_file'}
                  }
  1. You need to create these new columns in your migrations or you’ll get an exception. Example:

add_column :users, :avatar_file, :binary
add_column :users, :avatar_medium_file, :binary
add_column :users, :avatar_thumb_file, :binary

Note the “binary” migration will not work for the LONGBLOB type in MySQL for the file_contents column. You may need to craft a SQL statement for your migration, depending on which database server you are using. Here’s an example migration for MySQL:

execute 'ALTER TABLE users ADD COLUMN avatar_file LONGBLOB'
execute 'ALTER TABLE users ADD COLUMN avatar_medium_file LONGBLOB'
execute 'ALTER TABLE users ADD COLUMN avatar_thumb_file LONGBLOB'
  1. To avoid performance problems loading all of the BLOB columns every time you access

your ActiveRecord object, a class method is provided on your model called “select_without_file_columns_for.” This is set to a :select scope hash that will instruct ActiveRecord::Base.find to load all of the columns except the BLOB/file data columns.

If you’re using Rails 2.3, you can specify this as a default scope:

default_scope select_without_file_columns_for(:avatar)

Or if you’re using Rails 2.1 or 2.2 you can use it to create a named scope:

named_scope :without_file_data, select_without_file_columns_for(:avatar)
  1. By default, URLs will be set to this pattern:

/:relative_root/:class/:attachment/:id?style=:style

Example:

/app-root-url/users/avatars/23?style=original

The idea here is that to retrieve a file from the database storage, you will need some controller’s code to be executed.

Once you pick a controller to use for downloading, you can add this line to generate the download action for the default URL/action (the plural attachment name), “avatars” in this example:

downloads_files_for :user, :avatar

Or you can write a download method manually if there are security, logging or other requirements.

If you prefer a different URL for downloading files you can specify that in the model; e.g.:

has_attached_file :avatar, :storage => :database, :url => '/users/show_avatar/:id/:style'
  1. Add a route for the download to the controller which will handle downloads, if necessary.

The default URL, /:relative_root/:class/:attachment/:id?style=:style, will be matched by the default route: :controller/:action/:id

Defined Under Namespace

Modules: ControllerClassMethods

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.extended(base) ⇒ Object



315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/paperclip/storage.rb', line 315

def self.extended base
  base.instance_eval do
    @file_columns = @options[:file_columns]
    if @url == base.class.default_options[:url]
      @url = ":relative_root/:class/:attachment/:id?style=:style"
    end
  end
  base.class.interpolations[:relative_root] = lambda do |attachment, style|
    begin
      if ActionController::AbstractRequest.respond_to?(:relative_url_root)
        relative_url_root = ActionController::AbstractRequest.relative_url_root
      end
    rescue NameError
    end
    if !relative_url_root && ActionController::Base.respond_to?(:relative_url_root)
      relative_url_root = ActionController::Base.relative_url_root
    end
    relative_url_root
  end
  ActiveRecord::Base.logger.info("[paperclip] Database Storage Initalized.")
end

Instance Method Details

#assign(uploaded_file) ⇒ Object



386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
# File 'lib/paperclip/storage.rb', line 386

def assign uploaded_file

  # Assign standard metadata attributes and perform post processing as usual
  super

  # Save the file contents for all styles in ActiveRecord immediately (before save)
  @queued_for_write.each do |style, file|
    instance_write_file(style, file.read)
  end
  
  # If we are assigning another Paperclip attachment, then fixup the
  # filename and content type; necessary since Tempfile is used in to_file
  if uploaded_file.is_a?(Paperclip::Attachment)
    instance_write(:file_name,       uploaded_file.instance_read(:file_name))
    instance_write(:content_type,    uploaded_file.instance_read(:content_type))
  end
end

#column_for_style(style) ⇒ Object



337
338
339
# File 'lib/paperclip/storage.rb', line 337

def column_for_style style
  @file_columns[style.to_sym]
end

#exists?(style = default_style) ⇒ Boolean

Returns:

  • (Boolean)


363
364
365
# File 'lib/paperclip/storage.rb', line 363

def exists?(style = default_style)
  !file_contents(style).nil?
end

#file_contents(style = default_style) ⇒ Object Also known as: data



358
359
360
# File 'lib/paperclip/storage.rb', line 358

def file_contents(style = default_style)
  instance_read_file(style)
end

#flush_deletesObject



418
419
420
# File 'lib/paperclip/storage.rb', line 418

def flush_deletes
  @queued_for_delete = []
end

#flush_writesObject



414
415
416
# File 'lib/paperclip/storage.rb', line 414

def flush_writes
  @queued_for_write = {}
end

#instance_read_file(style) ⇒ Object



341
342
343
344
345
346
347
348
349
# File 'lib/paperclip/storage.rb', line 341

def instance_read_file(style)
  column = column_for_style(style)
  responds = instance.respond_to?(column)
  cached = self.instance_variable_get("@_#{column}")
  return cached if cached
  # The blob attribute will not be present if select_without_file_columns_for was used
  instance.reload :select => column if !instance.attribute_present?(column) && !instance.new_record?
  instance.send(column) if responds
end

#instance_write_file(style, value) ⇒ Object



351
352
353
354
355
356
# File 'lib/paperclip/storage.rb', line 351

def instance_write_file(style, value)
  setter = :"#{column_for_style(style)}="
  responds = instance.respond_to?(setter)
  self.instance_variable_set("@_#{setter.to_s.chop}", value)
  instance.send(setter, value) if responds
end

#path(style = default_style) ⇒ Object



382
383
384
# File 'lib/paperclip/storage.rb', line 382

def path style = default_style
  original_filename.nil? ? nil : column_for_style(style)
end

#queue_existing_for_deleteObject



404
405
406
407
408
409
410
411
412
# File 'lib/paperclip/storage.rb', line 404

def queue_existing_for_delete
  [:original, *@styles.keys].uniq.each do |style|
    instance_write_file(style, nil)
  end
  instance_write(:file_name, nil)
  instance_write(:content_type, nil)
  instance_write(:file_size, nil)
  instance_write(:updated_at, nil)
end

#to_file(style = default_style) ⇒ Object Also known as: to_io

Returns representation of the data of the file assigned to the given style, in the format most representative of the current storage.



369
370
371
372
373
374
375
376
377
378
379
# File 'lib/paperclip/storage.rb', line 369

def to_file style = default_style
  if @queued_for_write[style]
    @queued_for_write[style]
  elsif exists?(style)
    tempfile = Tempfile.new instance_read(:file_name)
    tempfile.write file_contents(style)
    tempfile
  else
    nil
  end
end