Vixiom Axioms

July 26, 2006

Rails StringIO File Upload

Filed under: Ruby on Rails Alastair @ 12:09 am

# UPDATE

It turns out the method below works unless your file is below a certain file size (which I haven’t figured out yet but it’s around 15k). If your file is too small it will be StringIO not a Tempfile. So how do we check it then? By the file size, if it’s 0 then don’t run the image upload code.
Replace

@image = Image.create params[:image] unless params[:image][:file_data].kind_of? StringIO

with

@image = Image.create params[:image] unless params[:image][:file_data].size == 0

# END UPDATE
Coming from PHP getting file uploads to work with Rails was a bit of a mind funk.

The biggest issue is that a file upload field returns different objects depending on if you browsed for a file or left it blank. If you browsed and chose a file the object will be a ‘Tempfile’, if it’s blank it will be ‘StringIO’. That took a couple of hours to figure out.

The next step was figuring out how to check the object type, that’s when I stumbled upon some beauty Ruby code

object.kind_of?

Simple once you know how.

The file field from the form in my view:

<%= file_field("image", "file_data") %>

Here’s my controller code for updating data from the form (the controller is products_controller.rb so I’m setting a relationship to the Image ‘product_id’):

def update #save image
  params[:image][:product_id] = params[:id]
#upload it unless it's StringIO
  @image = Image.create params[:image] unless params[:image][:file_data].kind_of? StringIO #save product params
  @product = Product.find(params[:id])
#update params that have changed
  if @product.update_attributes(params[:product])
#flash and redirect
    flash[:notice] = 'Product was successfully updated.' redirect_to :controller => 'cms', :action => 'index' else render :action => 'edit' end end

The line to notice is

    @image = Image.create params[:image] unless params[:image][:file_data].kind_of? StringIO

It translates to; save the image model unless file_data is a StringIO object (if it’s not StringIO then it’s a Tempfile)

Digg! submit Rails StringIO File Upload to stumbleupon.com submit Rails StringIO File Upload to del.icio.us submit Rails StringIO File Upload to reddit.com Like this post? subscribe to the feed.

9 Comments »

  1. This post helped me out more than you know.

    I’m not sure why file uploading in Rails is such a black-box area filled with weird special cases, but it’s nice to know I wasn’t the only one with the problem.

    Comment by Adam T. — November 26, 2006 @ 10:29 pm

  2. Definitely a big help. I was having trouble getting firefox tell whether a file was really being uploaded, and this was the path…thanks!

    Comment by ira S. — December 3, 2006 @ 7:22 pm

  3. i also encountered the same problem as you, but you could try

    @image = Image.create params[:image] unless params[:image][:file_data].size == 0

    there are .size method for both StringIO and TempFile, and it return zero if it is empty

    Comment by chiamingen — February 22, 2007 @ 11:58 pm

  4. If you’re file is small, you get a StringIO object. So only saving if you don’t have StringIO will not save small files.

    Comment by Bart — March 14, 2007 @ 10:51 pm

  5. Like Bart said, I am having that problem now. Some files actually come across as StringIO. I don’t know if it is based on the File Type or the Size of the file, but I can’t figure out how to get it back as a file object.

    Comment by Tom — May 2, 2007 @ 12:26 pm

  6. It is indeed the case that small files are StringIO objects while larger files get passed as a Tempfile object. This is done for performance reasons (small files can be kept in the buffer since they don’t take up too much space there, this saves us from filesystem operations which always carry some overhead (disk seek and the like)).

    A universal way to process the uploaded data is:
    File.open(”your/file/goes/here”, “wb”) { |f| f.write(params[:your_file_form_field].read) }

    No matter whether you are working with a Tempfile or StringIO object, the CGI handler always provides params[:your_file_form_field].original_filename and .content_type for your processing pleasure.

    Comment by Niels — July 22, 2007 @ 8:36 am

  7. Thanks a lot Niels! That’s exactly what I needed. I couldn’t figure out for the life of me how to deal with small files that come in as StringIO objects.

    Comment by Eric — October 2, 2007 @ 10:05 am

  8. [...] http://blog.vixiom.com/2006/07/26/rails-stringio-file-upload/ [...]

    Pingback by Rails and file uploads « 41 technologies — April 9, 2008 @ 10:05 pm

  9. Thanks a lot.
    What i had to add was a nil check, cause it throwed an exception when accessing the ’size’ property.

    params[:image][:file_data].nil?

    cheers

    daniel

    Comment by Daniel — February 26, 2009 @ 3:41 pm

RSS feed for comments on this post. TrackBack URL

Leave a comment

*
To prove you're a person (not a spam script), type the security word shown in the picture. Click on the picture to hear an audio file of the word.
Click to hear an audio file of the anti-spam word

Powered by WordPress