This is straight from the file_column website:
Just create a database column to store the filename:
add_column :entry,:image,:string
Then make the “image” column ready for handling uploaded files…
class Entry < ActiveRecord::Base
file_column :image
end
... generate file fields that keep uploaded images during form redisplays to your view:
<% @entry = Entry.find(params[:id]) %> <%= file_column_field "entry", "image" %>
... and display uploaded images in your view…
<% @entry = Entry.find(params[:id]) %> <%= image_tag url_for_file_column("entry", "image") %>
It’s just as easy! Why should it be any more difficult for a Rails application?
So what about the RMagick integration? Have a look:
To resize every uploaded image to a maximum size of 640×480, you just have to declare an additional option.
class Entry < ActiveRecord::Base
file_column :image, :magick => { :geometry => "640x480>" }
end
You can even automatically create versions in different sizes that have nice filenames…
class Entry < ActiveRecord::Base
file_column :image, :magick => {
:versions => { "thumb" => "50x50", "medium" => "640x480>" }
}
end
... and display them in your view:
<% @entry = Entry.find(params[:id]) %> <%= image_tag url_for_file_column(@entry, "image", "thumb") %>
NOTE: Your form has to be multipart/form-data, which is done like this:
<%= form_tag( { :action => 'do_image_upload' }, :multipart => true ) %>
and not like this:
<%= form_tag( :action => 'do_image_upload', :multipart => true ) %>
and not like this, either:
<%= form_tag { :action => 'do_image_upload' }, :multipart => true %>
Thanks to Greg @
http://destiney.com/blog/rails-form-tag
Note by a coward:
Actually you should do none of these, according to the docs. There should not be any ”=”. The real way should be (according to all I know):
<% form_tag( {:action=> 'do_image_upload' }, :multipart=> true ) do %> ........ <% end %>.
And don’t forget the word “do”!!
One thing remains a mystery to me: How to use a named form (which you may need to do if you have more than one form on a page) without using the deprecated form
<%= start_form_tag( {:action => 'do_save_all' }, {:name=>"my_2nd_form", :id=>"my_2nd_form", :multipart => true}) %> ........... <%= end_form_tag %>.
We hacked file_column to support storing to Amazon S3. It’s not perfect, but should be a good start for anyone that wants it:
Amazon S3 support for file_column
You can set the storage base path with the :store_dir option:
file_column :image, :store_dir => “foobar”
As for the path prefix, you could dynamically redefine the method FileColumn::PermanentUploadedFile#relative_path_prefix somewhere in your application code. The default implementation is pretty straight-forward
def relative_path_prefix
@instance.id.to_s
end
so just replace ‘id’ with ‘username’
In my case, I wanted my images to be stored in a folder with the login field of my User object. Problem is, my image column is in another table called Profile. Since each User has_many Profiles and Profile belongs_to User, I couldn’t just change @instance.id to @instance.login, because in my case @instance is my Profile object and not my User object.
To work around this, I had to use the Profile.user_id value to find the proper User object and get the User.login field. Here is my code which I placed at the bottom of my environment.rb file:
module FileColumn
class PermanentUploadedFile
def initialize(*args)
super *args
user = User.find(@instance.user_id) # change
@dir = File.join(store_dir, user.login.to_s) # change
@filename = @instance[@attr]
@filename = nil if @filename.empty?
end
def relative_path_prefix
user = User.find(@instance.user_id) # change
user.login.to_s # change
end
end
end
Probably not the most efficient code, but it works for me. If there is a cleaner way to do this, please update this Wiki.
Thanks to Gerret from the mailing list for his help.
FileUtils.mkdir(@dir) unless File.exists?(@dir) FileUtils.mkpath(@dir) unless File.exists?(@dir)just a note about using url_for_file_column inside an iteration
url_for_file_column(@entry, “image”, “thumb”)
<% for entry in @entries %>
<%= <b style="color:black;background-color:#ffff66">url_for_file_column</b>(entry, "image") %>
<% end %>
When using url_for_file_column in a partial you need to assign the local variable into an instance variable before calling the method.
<% @item = item -%>
I think you mean:
<% item = @item -%>
Also try this if above does not work for url_for_file_column in a partial:
First go into your file_column_helper.rb file and change the following code:
”rails_app\vendor\plugins\file-column\lib\file_column_helper.rb”
Before:
def url_for_file_column(object_name, method, suffix=nil)
object = instance_variable_get("@#{object_name.to_s}")
...
After:
def url_for_file_column(object_name, method, suffix=nil)
object = case object_name
when String, Symbol
instance_variable_get("@#{object_name.to_s}")
else
object_name
end
...
Then change your partial code to this:
_partial.rhtml
<% for post in @post %>
<%= link_to (image_tag(url_for_file_column(post, "image", "square")))%>
...
Don’t forget to restart your server after editing the ”/file_column_helper.rb” file
(by file_column author Sebastian Kanthak)
because url_for_file_column expects the model in an instance
variable, as stated in the docs as well. This is a rails convention
that you’ll find in other helpers as well, for example text_field. I
simply followed this convention, but lots of people seem to have
trouble with it. Perhaps, I’ll come up with something else…
Uploading files to a directory outside of the RAILS_ROOT
Has anyone had any luck using FileColumn to upload files to a location outside the RAILS_ROOT?
Re uploading files outside of the Rails root, if you look inside vendor/plugins/file_column/lib/file_column.rb about halfway down the file a long block of documentation begins. It describes there how to set custom storage locations:
== Custom Storage Directories
#
# FileColumn's storage location is determined in the following way. All
# files are saved below the so-called "root_path" directory, which defaults to
# "RAILS_ROOT/public". For every file_column, you can set a separte "store_dir"
# option. It defaults to "model_name/attribute_name".
#
# Files will always be stored in sub-directories of the store_dir path. The
# subdirectory is named after the instance's +id+ attribute for a saved model,
# or "tmp/<randomkey>" for unsaved models.
#
# You can specify a custom root_path by setting the <tt>:root_path</tt> option.
#
# You can specify a custom storage_dir by setting the <tt>:storage_dir</tt> option.
#
# For setting a static storage_dir that doesn't change with respect to a particular
# instance, you assign <tt>:storage_dir</tt> a String representing a directory
# as an absolute path.
#
# If you need more fine-grained control over the storage directory, you
# can use the name of a callback-method as a symbol for the
# <tt>:store_dir</tt> option. This method has to be defined as an
# instance method in your model. It will be called without any arguments
# whenever the storage directory for an uploaded file is needed. It should return
# a String representing a directory relativeo to root_path.
#
# Uploaded files for unsaved models objects will be stored in a temporary
# directory. By default this directory will be a "tmp" directory in
# your <tt>:store_dir</tt>. You can override this via the
# <tt>:tmp_base_dir</tt> option.
I had trouble getting this done following the various instructions from the diff apps, so i wanted to keep a record of how i got it working in the end. (NB: I’m a RoR/command line newbie so please correct/alter if this is a weird way of doing things.)
First install file_column by running the following from the command prompt:
ruby script/plugin install [ insert latest url from file_column site]
Get the most current url from the instructions on the file_column site.
Next you’ll need to install RMagick. RMagick is an interface between Ruby and the image manipulation applications, ImageMagick and GraphicsMagick. Conveniently, the windows download contains RMagick itself, as well as ImageMagick.
Download ‘rmagick-win32 x.x.x binary gem’ from the RMagick download page
Once downloaded use ‘cd’ to navigate to the newly downloaded directory containing the .gem file. Then run the following command (replacing the x’s with the correct version number):
gem install RMagick-win32-1.x.x-mswin32.gem
Once that’s done you need to run the following command (in the same directory) to finish the job:
postinstall.rb
(I couldnt find a link to full documentation about file_column, pls edit and place link/instructions here)
Now the following directory should be present in your rails app myApp/vendor/plugins/file_column.
Decide which model you want to use to store the uploaded image info. Choose a field in that model and set its type to varchar or text so that it can contain long filenames.
Next: In my case i added this line to the .rb file for my model:
file_column :image, :magick => { :geometry => "640x480>",
:versions => { "thumb" => "50x50"}
}
This tells my app that the field ‘image’ in my model should be used to store filenames of uploaded images.
The versions array creates a resized copy of the image with the name ‘thumb’ in this case.
To write the form where users can upload images use something like the following to make sure the form is a multi-part type:
<%= form_tag({:action=>'create_art_image', :id=> @art_image}, :multipart => true)%>
Use something like the following line to add the file browse button (‘art_image’ is the name of the model holding the image information):
<%= file_column_field "art_image", "image" %><br/>
@art_image = ArtImage.new(@params[:art_image])
By default the fullsize images get saved to:
myApp/public/[Model name]/[file_column field name]/[record id]/[image filename]
And the ‘thumb’ versions end up in:
myApp/public/[Model name]/[file_column field name]/[record id]/thumb/[image filename]
To display the thumbnails in my views i use:
<%= image_tag <b style="color:black;background-color:#ffff66">url_for_file_column</b>(@art_image, "image", "thumb") %>
Gotchas
$HTTP["useragent"] =~ "^(.*MSIE.*)|(.*AppleWebKit.*)$" {
server.max-keep-alive-requests = 0
}
Putting all files under one subdirectory
I wanted all of my files to be stored under public/files. It took me a while to figure out I needed to pass the :root_path option to get it to save there, and the :web_root option to make the helpers build the correct url:
file_column :image, :web_root => "files/", :root_path => File.join(RAILS_ROOT, "public", "files")
Is there a better way to do this?
In the rdocs of the current version of filecolumn (0.3.1) there’s an example given of how to create square thumbnails using the MagickExtension commands, but for me that example didnt give the expected results (the ‘crop’ part seemed to be being ignored and the thumbnails kept the original images width/height ratio).
Adding the ’!’ modifier to the size parameter for the thumbnail version gave me the expected result:
class ArtImage < ActiveRecord::Base
belongs_to :artwork
file_column :image, :magick => {:versions => {
:thumb => {:crop => "1:1", :size => "50x50!", :name => "thumb"},
:normal => {:size => "500x500>"}
}
}
end
More information about the geometry string here
How do I delete an entry in a file_column? Just delete the associated model. The file_column is just an annotation on a field in the model.
—But, what about if you didn’t create a new model just for the file_column uploads, but instead added a new column to an existing model? I don’t want to delete my entire user object, just the image files each user can upload via file_column…
Set the model’s file_column field to nil. Saving the model will delete the associated files.
How would one go about allowing the user upload multiple images at once?
A great resource for doing this can be found here: http://the-stickman.com/web-development/javascript/upload-multiple-files-with-a-single-file-element/
I adapted a version of the multiple file upload script(newest mootools version) so it works with file_column. Complete details and instructions can be found at:
http://www.silverscripting.com/blog/2007/12/18/multiple-file-upload-with-rails-file_column-plugin-and-mootools/
It seems like RMagick causes a lot of headaches for people who want to use file_column. Are there any implementations of file_column without RMagick?
Using file_column without specifying thumbnail options should work without RMagick
I asked this question on the Rails forum and the best reply quoted the Caboose site. I’ve repeated it here:
For a definition of:
class Attachments < ActiveRecord::Base
file_column :asset, :magick => {
:versions => { "thumb" => "50x50", "medium" => "640x480>" }
}
end
Backup your attachments first, then open up script/console and…
Attachment.find(:all).each do |a|
a.asset = File.open(a.asset)
a.save
end
I haven’t tested it, mind. I ended up writing a far heavier, more klunky script before I got the reply.
The caboose solution does not work as-is… a more complete solution can be found at how to regenerate file_column images
How to save the image width and height in the database
Put this in your create action:
image = Magick::Image::read(@entry.image).first @entry.imagewidth = image.columns @entry.imageheight = image.rows
Running Rails tests (unit, functional, etc., as well as selenium tests) will drop and reload test DB content, but they do not touch the disk files created by file_column.
What is the right way to handle this?
Your functional and integration tests don’t need to upload real files. file_column provides some mock objects for you to use in your tests. Have a look. http://opensvn.csie.org/rails_file_column/plugins/file_column/trunk/lib/test_case.rb
What is the proper way to upload an image to the filesystem, and save a version of the image that has (for example) been processed through the rmagick polaroid example?
@user.update_attribute("picture", params[:user][:picture])
... it completely ignores the validations I’ve set for the element
before:
validates_file_format_of :picture, :in => ["gif", "jpg", "png"] validates_filesize_of :picture, :in => 1.kilobytes..100.kilobytes
I’m able to upload PDF’s, textfiles, etc., which is ofcourse rather
crappy. I’ve added this to my model, in case you’re wondering:
file_column :picture, :base_url => "images/people", :filename =>
"picture", :store_dir => "public/images/people/",
:magick => {
:size => '48x48!',
:crop => '1:1',
:versions => {
:thumb => {:crop => "1:1", :size => "16x16!", :name => "thumb"}
}
}
Has anybody else experienced this problem?
Answer: update_attribute() is designed not to use validations. see the ActiveRedcord API doc. Try update_attributes instead.
I get error when uploading an image file with .JPG (capital JPG) extension.
If I change “JPG” to “jpg” in the file_column field and then submit the form, everything works fine.
How can I avoid this error?
I don’t have a fix to this, but I do have a very, very ugly workaround. I’m very green with Rails (and Ruby) and so took it upon myself as educational exercise into the bowels of someone else’s code. I added the code (in bold) after the line listed below in TempUploadFile#store_upload(file) in file_column.rb:
@filename = FileColumn::sanitize_filename(file.original_filename)
<b>@filename.downcase!</b>
It seems to do the trick – the file is stored in the file system and in the database with the lowercase name. Using lowercase file names gets around the problem. Of course, if your OS is case-sensitive and you’ve already upload another file that happens to be the same name (that is in lower case), you’re going to get into trouble.
I haven’t delved into this problem, but it seems to be an issue within FileUtils and its copying methods.
—
How can I use file_column while still ensuring that only certain users are allowed to access files? If everything is stored in /public, then anyone can access it (if they know the file name or start url hacking). What I need is to be able to use authentication type controls to see if someone is authorized to download a file.
If it’s not stored in RAILS_ROOT/public – e.g. if it’s in RAILS_ROOT/s3kr1t_f1l3z :) – then it would need to get served somehow by the Rails app itself…?
What about defining a custom route in routes.rb which sends you thru some sort of authorization checker then serves the file? You wouldn’t have to modify file_column at all for that… but I don’t know if it’s possible…
This can be done by storing files in the database. This way, they will be served via a controller, so you can add all kinds of authorization you want. Check fleximage
—
The filenames themselves are stored in the database, now, whenever I try to print the filename ONLY, for instance:
<%= artwork.file %>
returns a long path to the file when it used to give me just “blahblah.jpg”
How do I just make it act the way it used to?
- Try adding ”_before_type_cast” to your column name. E.g.:
<%= artwork.file_before_type_cast %>
It’s probably the problem with permissions. I edited the move_from function in file_column.rb to make it look like this (look at the first if statement):
def move_from(local_dir, just_uploaded)
# create a directory named after the primary key, first
unless File.exists?(@dir)
FileUtils.mkdir(@dir)
FileUtils.chmod_R 0755, @dir
end
# move the temporary files over
FileUtils.cp Dir.glob(File.join(local_dir, "*")), @dir
@just_uploaded = just_uploaded
# remove all old files in the directory
FileUtils.rm(Dir.glob(File.join(@dir, "*")).reject! {
|e| File.exists?(File.join(local_dir, File.basename(e)))
})
end
Can someone with commit-rights to the trunk please add the :web_root option to the “official” documentation in this plugin? I’ve now spent 4 hours trying to fix it another way – and then realizes that there’s already an option.
I´ve been searching for this some days from now and havent found anything. I saw in one forum a guy who tried setting the database filename column for the record to “NULL” or ”” and that did not work for the user so he had to build separate models the pictures, which I don´t want to do unless there is no other option.
I would appreciate any help. Thanks!