attachment_fu – working with database file storage

Once I had attachment_fu working with files stored within the Rails application file system, I hit a problem as I wanted to be able to share files across three applications. The problem being that if the file was uploaded in one application, it would be stored within that application’s public folder. Without adding complication, this location wasn’t available to the other applications.

The solution was to change over to storing the files within the database. This took a couple of small modifications:

model
I changed the has_attachment declaration to:

has_attachment :max_size => 50.megabyte

The default attachment_fu is to use the database to store files. So removing the :storage entry in this statement was enough to change over from file system storage to database storage.

database
I has to add a new table and an additional column to my main model. The new table was created with:

    create_table :db_files do |t|
      t.column :data, :binary, :limit => 50.megabyte
    end

Note that adding a :limit entry can alter the blob field type created in the database. Without it the standard MySQL behaviour is to create an ordinary blob field. If the :limit statement indicated that a blob isn’t big enough, the data field will be set as a LONGBLOB.

The extra field added to my product_files table was ‘db_file_id’ which is an integer field.

public_filename
When working with database file storage, public_filename is no longer available. So I needed to come up with an alternative way to serve the file to the user.

controller download action
To do this I created a download action.

  def download
    if params[:id] and ProductFile.exists?(params[:id])
      product_file = ProductFile.find(params[:id])
      send_data(
        product_file.current_data,
        :type => product_file.content_type,
        :filename => product_file.filename
      )
    else
      flash[:error] = "Product file not found"
      redirect_to :action => 'index'
    end
  end

Note that send_data is a standard Rails method, so its use can be looked up in the Rails api. I wasted a little time trying to find this in the attachment_fu code.

helper
It was then just a case of creating a helper that pointed at this action.

  def link_to_download(product_file)
    link_to(
      'Download',
      :controller => 'product_files',
      :action => 'download',
      :id => product_file.id
    )
  end

view
This could then be used in a view, wherever I need a link to download a file I used it like this:

 <%= link_to_download(@product_file) -%>

Job done (except writing the tests of course)

This entry was posted in Ruby. Bookmark the permalink.