Ruby on Rails File Upload



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.



7 Responses to “Ruby on Rails File Upload”

  1. [...] I’ve been digging around for a while trying to find a way for Ruby on Rails to access the command line. To be more specific, I’ve been trying to get RoR to kick off an FFmpeg process. My objective has been to allow video uploads through a rails app and then kick off a process to convert the file to flv format. [...]

  2. Joe Cairns says:

    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

  3. dennis says:

    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.

  4. vash says:

    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!!!

  5. #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) }

  6. Jason Gill says:

    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?

  7. 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

Leave a Reply