Thursday 18 March 2010

Capturing File Uploads

Here is the requirement: The web page asks the user to upload a file. The data in the file is then used to populate a new record in the database.

The first thing I want, then, is for the view to have a file upload button. Here is what worked for me:
<% form_tag 'create', :multipart => true do %>
<%= hidden_field_tag :sample_id, @sample.id %>
<p>
<%= file_field_tag 'datafile' %>
</p>
<p>
File names can only contain numbers, letters, hyphens and underscores, and must end ".csv".
Files must be on your C: drive to be successfully uploaded.
</p>
<p>
<%= submit_tag "Okay" %>
</p>

You could also use the form_for method instead, with file_field, rather than file_field_tag.

Some points to note:

You need to flag the form as "multipart", so that the file gets submitted as one part, and other data on the form as a second part. My experience is that uploaded files are a little restricted, and will not upload from a networked drive, or if there are strange characters in the name, and I warn the user of this. This may be dependant on your system. In this particular case, I am looking for a .csv file. The file type may need to be defined as a MIME type (CSV is registed by default).

The file received is of the UploadedTempfile type, and seems to be useable like any other file. As far as I can tell, Rails closes and deletes it for you. It is accessible though params in the normal way (so as params[:datafile] from the above code).
params[:datafile].each_line do |s|
# do stuff
end

I could not find away to process a CSV file using the CSV library; it seems to demand a filename, rather than the file itself. I was obliged to do it by hand (in my case I knew every field would be surrounded by quotes, so it is pretty easy).

In the functional test, just send the file as a parameter (with a test file in the fixtures folder).
post :create, :sample_id => Sample.find(:first).id,
:datafile => File.new("#{RAILS_ROOT}/test/fixtures/results.csv")



Struggling with Ruby: Contents Page

No comments: