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:
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:
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.
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.
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:
Job done (except writing the tests of course)