If you haven't tried uploading files in rails, you are in for a treat. Most of the time a feature is implemented that everything is done with reasonable defaults and just works. Uploading files, this is simply not the case.
Follow up:
If you upload a file from a form and it is under a magic size (appears to be 15-20kbytes) then the object is given to you as StringIO. If it is over this size, then it is given to you as a TempFile. So now whenever you have to implement an upload function in your app, you have to have a section of code that checks the type of the object and handles it in a different manner.
So if you are going to take these uploads and deal with them on the filesystem, you will need to make sure you have tempfiles. Here is how you would think you would do it:
if uploadedFile.kind_of? StringIO
@realFile = Tempfile.new("AppName")
while not uploadedFile.eof?
@realFile.write uploadedFile.read
end
else
@realFile = uploadedFile
end
This bit of code works, but there is one problem. If the file is a binary file, that is under the magic file size, it comes in as a StringIO and when written to the disk this way it is corrupted.
My first guess was that somehow it was treating it as string data instead of binary data and messing things up. So this was what I tried:
if uploadedFile.kind_of? StringIO
uploadedFile.binmode
@realFile = Tempfile.new("AppName")
while not uploadedFile.eof?
@realFile.write uploadedFile.read
end
else
@realFile = uploadedFile
end
Note the 2nd line added there. Turns out that this *still* doesn't work. The results are the same. The real solution is that you have to do a rewind on the StringIO before reading. Why it is not passed to us in a fresh state ready to real is a mystery to me.
The final working solution is here:
if uploadedFile.kind_of? StringIO
uploadedFile.rewind
@realFile = Tempfile.new("AppName")
while not uploadedFile.eof?
@realFile.write uploadedFile.read
end
else
@realFile = uploadedFile
end
In my opinion, this should be done by default and all files uploaded should always be returned as a tempfile. This way you don't have any extra code trying to figure out what really is passed to you when you are asking for a file upload.
By default Ruby open files in text mode ('w'), that works under POSIX but under Windows, you need the binary 'b' for the mode or call #binmode as you did.
There are several patches for Tempfile under Windows, some of those came from Rails, try googling for that if you need more info.
The part here that is confusing me is why I should need to do a rewind on the StringIO before reading it. That and the fact that you have to deal with uploads in 2 ways depending on the file size.
I always get a weird error trying that...
In the migration:
t.column :myFile, :binary
To save it, you would do something along the lines of:
myObject.myFile = @realFile.read
brian@briansblog.net