Image uploads in Rails

Hey guys! Things are going great for me - thanks in no small part to all
the help i received here. I finished my site, and I’ll put up the url if
people want to check it out.
But that’s not the reason I’m posting today. I’m working on my next
site, and I realized that I’m going to need to add image upload
functionality to this one. My Ruby skills are minimal at this point, so
I’m trying to find the most painless way to do this; i tried the
file_column plugin, but it doesn’t seem to do what it says it will do:
it generates the html code for the upload ok but seems to stop there,
and offers few clues how to write a controller method for it to link to;
also the way the image is stored seems odd. I looked up the file upload
how-to on the Rails site, but it seems very bare-bones as well.

Does anybody have a favorite method for this?

Hey Sean,

you might want to check out the Flex Image plugin available at:
http://beautifulpixel.com/
The plugin is an absolultely fantastic and I’m sure you’ll get a grip
on it very fast. I find FlexImage to be way more easier to use than
file_column. So give it a try.

Cheers,
Mike.

Thanks man. I checked it out and it looks tight. Just have to install
“RMagick”, which looks like a headache and a half. Looks like it will
have to wait for tomorrow when I can find my XCode tools CD.

Just out of curiosity, are there any reliable methods out there that
don’t require RMagick?

I’d recommend using MacPorts for ruby and rb_rmagick installations. It
simply works.

MacD wrote:

Hey Sean,

you might want to check out the Flex Image plugin available at:
http://beautifulpixel.com/
The plugin is an absolultely fantastic and I’m sure you’ll get a grip
on it very fast. I find FlexImage to be way more easier to use than
file_column. So give it a try.

Cheers,
Mike.

Thank you, Mike!

Although, if you don’t need to resize or process the image in any way,
then you don’t need FlexImage, and dont need RMagick.

The way the plugin handles uploads, however, could be easily reused.
The basic idea is to override the property setter for the binary data on
your model. Then the model handles writing it to the file system or
database.

#model
def image=(file)
File.open("#{RAILS_ROOT}/public/images/#{file.original_filename}",
“w+”) do |f|
f.write file.read
end
end

#view
<%= form_tag({:action => ‘upload’}, {:multipart => true}) %>
<%= file_field ‘avatar’, ‘image’ %>
<%= submit_tag ‘Upload image!’
<%= end_form_tag %>

#controller
def upload
Avatar.create(params[:avatar])
end

But if you want to do any kind of processing or vlaidation on that
image, you need to install rmagick. RMagick is the only ruby library
out there that can read, process and write images.

And if you need to display images at different sizes or ensure uploads
do not exceed a certain image size, then FlexImage will make your life a
whole lot easier.

Wow. That’s very little code.
I love Rails.

Anyway, I know this is the kind of question that probably everybody
hates, but if anyone has the patience, could you break down a few points
in this technique for the non-programmer?

For example, this notation:

def image=(file)

I’d understand “def image(file)”, where image would be the function name
and file would be the parameter, but I’ve never seen an equals sign used
that way before. What does it do?

and this:

File.open("#{RAILS_ROOT}/public/images/#{file.original_filename}", 

“w+”) do |f|
f.write file.read

is “original_filename” real code or pseudocode?

this i get:

<%= form_tag({:action => ‘upload’}, {:multipart => true}) %>
<%= file_field ‘avatar’, ‘image’ %>
<%= submit_tag ‘Upload image!’
<%= end_form_tag %>

But this:

def upload
Avatar.create(params[:avatar])
end

this means that I would need an “Avatar” model, right? And the file
would just be stored in the images folder and no database activity would
take place?

Sean C. wrote:

Wow. That’s very little code.
I love Rails.

Absolutely

Anyway, I know this is the kind of question that probably everybody
hates, but if anyone has the patience, could you break down a few points
in this technique for the non-programmer?

For example, this notation:

def image=(file)

I’d understand “def image(file)”, where image would be the function name
and file would be the parameter, but I’ve never seen an equals sign used
that way before. What does it do?

adding an equals sign just makes an equal sign part of the method name.
You can also have ? and ! in your method names. What this does is when
you say:

SomeModel.image = params[:some_model][:image]

It will call the image= method with the argument of
params[:some_model][:image]. Ruby does not interpret the above code as
an assignment, but rather simply a method call. The reason this works
with:

SomeModel.create(params[:some_model])

Is that rails internally takes every attribute the hash that you provide
to the create method and calls the “=” method for it. So when you
write:

SomeModel.create({:foo => ‘bar’, :baz => ‘123’})

Rails internally executes:

model = SomeModel.new
model.foo = ‘bar’
model.baz = ‘123’
model.save

So when we define an “attribute=(value)” method it allows to wrap some
other processing around a simple assignment, such as saving an uploaded
image to the disk.

And as a minor note this magic also works with [] and []=

class Foo
def
“Passed in #{key.to_s.upcase}”
end
def []=(key, value)
“You tried to make the key #{key} the value of #{value}”
end
end

foo = Foo.new
foo[:doodle] #=> “Passed in Doodle”
foo[:gnarf] = ‘foo’ #=> “You tried to make the key gnarf the value of
foo”

and this:

File.open("#{RAILS_ROOT}/public/images/#{file.original_filename}", 

“w+”) do |f|
f.write file.read

is “original_filename” real code or pseudocode?

Totally real code. Look in to “Multipart Requests” section of the CGI
class documentation here: http://www.ruby-doc.org/core/classes/CGI.html

Uploaded files have a few extra methods, namely: orginal_filename,
local_path, and content_type

Although, you only need to use that if you care about the original
filename. If you are mapping them directly to a model you may want to
simply name the file with the id of the record it belongs to instead.

But this:

def upload
Avatar.create(params[:avatar])
end

this means that I would need an “Avatar” model, right? And the file
would just be stored in the images folder and no database activity would
take place?

Yes.

Or if you want it to write to a binary database field:

def image=(file)
self[:image] = file.read
end

the [] notation allows to read and write the attributes of your model if
you have ovveridden the normal accessor methods like we did here. And
the read method simply pulls all the data out of the file object as a
string, a big string.

However, this entire methodology was assuming you wanted to store an
image that directly relates to a record. If you just want to upload
random images to a folder in your web root, meaning without any model at
all, then its super easy.

Create a new file and write into it what you read out of the uploaded
file. This is a snippet from one of my apps:

#view
<%= file_field_tag ‘file’ %>

#controller
def upload
File.open
“#{RAILS_ROOT}/public/misc/#{params[:file].original_filename}”, ‘wb’ do
|f|
f.write(params[:file].read)
end
redirect_to :action => ‘index’
end

Wow. Thanks for the breakdown, Alex. You’re still a bit ahead of me, but
I can understand something like 70% of what you’re talking about. I’ll
look up the rest. For now, what I want to do is just overwrite the same
file, over and over. I’ve got, let’s say, a main graphic on my front
page. I don’t need to keep records of what previous versions of that
graphic were, so if I want to change the picture on my front page, all I
have to do is overwrite the image file with a new on of the same name
and refresh the browser.

For now, all I want to do is be able to change that image, over the web,
without using FTP. Later, once I get a handle on that basic idea, I will
be trying to implement more complicated functions, like associating
uploaded images with a blog entry, renaming them to a standardized
naming format using timestamps or something to keep them from
overwriting each other, and (hopefully) checking and resizing them upon
upload. But I know it will be some time and no small amount of studying
before I get there.

Anyway, thanks!

sean

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs