You may have a requirement in which you want your site visitors to upload a file on your server. Rails makes it very easy to handle this requirement. Now, we will proceed with a simple and small Rails project.
As usual, let's start off with a new Rails application called upload. Let's create a basic structure of the application by using simple rails command.
C:\ruby> rails -d mysql upload
Let's decide where you would like to save your uploaded files. Assume this is data directory inside your public section. So, create this directory and check the permissions.
C:\ruby> cd upload C:\ruby\upload> mkdir upload\public\data
Our next step will be as usual, to create controller and models.
As this is not a database-based application, we can keep whatever name is comfortable to us. Assume we have to create a DataFile model.
C:\ruby\upload> ruby script/generate model DataFile exists app/models/ exists test/unit/ exists test/fixtures/ create app/models/data_file.rb create test/unit/data_file_test.rb create test/fixtures/data_files.yml create db/migrate create db/migrate/001_create_data_files.rb
Now, we will create a method called save in data_file.rb model file. This method will be called by the application controller.
class DataFile < ActiveRecord::Base def self.save(upload) name = upload['datafile'].original_filename directory = "public/data" # create the file path path = File.join(directory, name) # write the file File.open(path, "wb") { |f| f.write(upload['datafile'].read) } end end
The above function will take the CGI object upload and will extract the uploaded file name using the helper function original_filename and finally, it will store the uploaded file into the "public/data" directory. You can call the helper function content_type to know the media type of the uploaded file.
Here File is a ruby object and join is a helper function that will concatenate the directory name along with the file name and will return the full file path.
Next, to open a file in write mode, we are using the open helper function provided by the File object. Further, we are reading data from the passed data file and writing into output file.
Now, let's create a controller for our upload project −
C:\ruby\upload> ruby script/generate controller Upload exists app/controllers/ exists app/helpers/ create app/views/upload exists test/functional/ create app/controllers/upload_controller.rb create test/functional/upload_controller_test.rb create app/helpers/upload_helper.rb
Now, we will create two controller functions. The first function index will call a view file to take user input, and the second function uploadFile takes file information from the user and passes it to the 'DataFile' model. We set the upload directory to the 'uploads' directory we created earlier "directory = 'data'".
class UploadController < ApplicationController def index render :file => 'app\views\upload\uploadfile.html.erb' end def uploadFile post = DataFile.save( params[:upload]) render :text => "File has been uploaded successfully" end end
Here, we are calling the function defined in the model file. The render function is being used to redirect to view file as well as to display a message.
Finally, we will create a view file uploadfile.rhtml, which we have mentioned in the controller. Populate this file with the following code −
<h1>File Upload</h1> <% form_tag ({:action => 'uploadFile'}, :multipart => true) do %> <p><label for="upload_file">Select File</label> : <%= file_field 'upload', 'datafile' %></p> <%= submit_tag "Upload" %> <% end %>
Here everything is same what we have explained in the earlier chapters. The only new tag is file_field, which will create a button to select a file from user's computer.
By setting the multipart parameter to true, you ensure that your action properly passes along the binary data from the file.
Here, an important point to note is that we have assigned "uploadFile" as the method name in :action, which will be called when you click the Upload button.
It will show you a screen as follows −
Now, you select a file and upload it. This file will be uploaded into app/public/data directory with the actual file name and a message will be displayed saying that "File has been uploaded successfully".
NOTE − If a file with the same name already exists in your output directory, then it will be overwritten.
Internet Explorer includes the entire path of a file in the filename sent, so the original_filename routine will return something like −
C:\Documents and Files\user_name\Pictures\My File.jpg
Instead of just −
My File.jpg
This is easily handled by File.basename, which strips out everything before the filename.
def sanitize_filename(file_name) # get only the filename, not the whole path (from IE) just_filename = File.basename(file_name) # replace all none alphanumeric, underscore or perioids # with underscore just_filename.sub(/[^\w\.\-]/,'_') end
If you want to delete any existing file, it is quite simple. All that you need to do is write the following code −
def cleanup File.delete("#{RAILS_ROOT}/dirname/#{@filename}") if File.exist?("#{RAILS_ROOT}/dirname/#{@filename}") end
For a complete detail on File object, you need to go through our Ruby Reference Manual.