Ruby on Rails File Upload
Jan 18
I’ve been experimenting a good bit with Ruby on Rails’ file upload capabilities. Overall I’ve been very impressed with the ease of use when it comes to uploading a file and writing it to disk. Of course anything is easy once you know how. Ruby’s syntax is very different than what you’re probably used to. While I’m by no means an expert on the subject I would definitely recommend the following books:
Programming Ruby: The Pragmatic Programmers’ Guide
Agile Web Development with Rails
After digging up the necessary code I was able to experiment with ROR’s file upload capabilities. My method called create was attached to my controller and looked like:
def create
File.open(“/Users/db/_dev/rails/videos/swing.mov”, “w”){|f|f.write(@params["video"].read)}
end
With that one line of code the file was written to disk….not too shabby. It’s right up there with ColdFusion’s CFFILE which has a much cleaner tag-based approach. At any rate, using this method allowed me to upload all sorts of photos and videos with ease.
I then proceeded to strain the server a little bit. When I say server ROR is running locally on my Powerbook. I grabbed a 175MB video file and put it to the test. The upload took almost 5 minutes to complete and during this time the output of top displayed ROR’s CPU utilization at about 80%. Pretty heavy lifting, but the upload competed successfully and I was able to view the uploaded file without any hiccups.

Hi, thanks for the tip on the file upload technique. One problem will crop up if you are developing on a windows machine.
The line:
File.open(”/Users/db/_dev/rails/videos/swing.mov”, “w”)
Should be changed to:
File.open(”/Users/db/_dev/rails/videos/swing.mov”, “wb”)
Windows, unlike linux, unix, and mac, does not assume a created file is a binary, it assumes a text file. By adding the “b” to the parameters you are forcing the file to be a binary. I don’t know whether using the “b” option will cause a non-windows install problems or not though.
-Joe
Hey Joe, thanks for the great info. I don’t normally develop my Rails apps on Windows, but will keep this in mind the next time I do. I’ll test the “b” parameter on my Mac and Linux to see if it causes any problems. If not, I’ll use it by default so that my code is compatible across different operating systems. Thanks again.
I really would want to see how you created everything, from the model, view and controller etc. I’m a rails noob and also would want to see full/complete code. Tnx a lot!!!
#create a unique file name using timestamp Month/Day/Year/Hour/Minute/Seconds and filename
newname= Time.now.strftime(“%m%d%y%H%M%S”).to_s +”#{file.original_filename}”
#with linux you may have to put the complete Path – also take the wb attribute and make it just ‘w’
File.open(“/path/to/folder/#{newname}”, “w”) { |f| f.write(file.read) }
OK, I’ve been playing around with this a good deal myself. The problem I’m having is with storing it in the database for later use. If I use multipart, the name of the file isn’t stored, instead it stores something like:
--- !ruby/object:StringIO {}Any ideas?
Jason, here is some code from the model of one of my applications. This saves the file to disk and then returns the file name, width, and height. I hope this helps and let me know if you have any questions.
def self.save_user_photo(photo, user_id)
time = Time.now.to_i
image_name = “#{user_id}_#{time}_#{rand(10000000)}”
image_path = “#{PHOTO_UPLOAD_DIR}#{image_name}”
# Write the original image to disk
File.open(“#{image_path}.jpg”, “wb”) do |file|
file.puts photo.read
end
# Create the default image
img = Magick::Image::read(“#{image_path}.jpg”).first
# Get the aspect ratio
aspect_ratio = img.columns.to_f/img.rows.to_f
# Create the profile photo with a max width of 480 and variable height based on aspect ratio
if img.columns.to_f > 640
new_height = (640/aspect_ratio).round
photo = img.scale(640, new_height)
else
photo = img.scale(img.columns, img.rows)
end
# Overwrite the uploaded photo with the smaller one
photo.write(“#{image_path}.jpg”)
# Create the thumbnail, don’t size it up if it’s too small
if img.columns.to_f > 200
thumb = img.scale(200, (200/aspect_ratio).round)
else
thumb = img.scale(img.columns, img.rows)
end
# Write the thumbnail photo
thumb.write(“#{image_path}_thumb.jpg”)
# Return image name, width, and height
[image_name, photo.columns, photo.rows]
end