Ruby on Rails 2.1 - 文件上传



您可能需要一个让网站访问者将文件上传到服务器的功能。Rails 使得处理此需求非常容易。现在,我们将继续进行一个简单的小型 Rails 项目。

像往常一样,让我们从一个名为 **upload** 的新 Rails 应用程序开始。让我们使用简单的 rails 命令创建一个应用程序的基本结构。

C:\ruby> rails -d mysql upload

让我们决定您希望将上传的文件保存到哪里。假设这是公共部分内的 **data** 目录。因此,请创建此目录并检查权限。

C:\ruby> cd upload
C:\ruby\upload> mkdir upload\public\data

我们的下一步将是像往常一样,创建控制器和模型。

创建模型

由于这不是一个基于数据库的应用程序,我们可以保留任何我们觉得方便的名称。假设我们必须创建一个 **DataFile** 模型。

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   

现在,我们将在 **data_file.rb** 模型文件中创建一个名为 **save** 的方法。此方法将由应用程序控制器调用。

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

上述函数将接收 CGI 对象 **upload**,并使用辅助函数 **original_filename** 提取上传的文件名,最后将上传的文件存储到“public/data”目录中。您可以调用辅助函数 **content_type** 来了解上传文件的媒体类型。

这里 **File** 是一个 Ruby 对象,**join** 是一个辅助函数,它将连接目录名和文件名,并返回完整的文件路径。

接下来,要以写入模式打开文件,我们使用 **File** 对象提供的 open 辅助函数。此外,我们正在读取传递的数据文件中的数据并写入输出文件。

创建控制器

现在,让我们为我们的上传项目创建一个控制器:

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

现在,我们将创建两个控制器函数。第一个函数 **index** 将调用一个视图文件来获取用户输入,第二个函数 **uploadFile** 从用户那里获取文件信息并将其传递给“DataFile”模型。我们将上传目录设置为我们之前创建的“uploads”目录“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

在这里,我们调用了在模型文件中定义的函数。**render** 函数用于重定向到视图文件以及显示消息。

Explore our latest online courses and learn new skills at your own pace. Enroll and become a certified expert to boost your career.

创建视图

最后,我们将创建一个视图文件 **uploadfile.rhtml**,我们在控制器中提到了它。使用以下代码填充此文件:

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

这里所有内容都与我们在前面章节中解释的内容相同。唯一的新标签是 **file_field**,它将创建一个按钮,用于从用户的计算机中选择文件。

通过将 multipart 参数设置为 true,您可以确保您的操作正确地传递文件的二进制数据。

这里需要注意的一点是,我们在 **:action** 中将 **"uploadFile"** 指定为方法名称,当您单击 **Upload** 按钮时将调用此方法。

它将向您显示如下屏幕:

Upload File

现在,您选择一个文件并上传它。此文件将以实际文件名上传到 app/public/data 目录中,并将显示一条消息,提示“文件已成功上传”。

**注意** - 如果输出目录中已存在同名文件,则将覆盖该文件。

从 Internet Explorer 上传的文件

Internet Explorer 在发送的文件名中包含文件的完整路径,因此 **original_filename** 例程将返回类似以下内容:

C:\Documents and Files\user_name\Pictures\My File.jpg

而不是:

My File.jpg

这很容易通过 **File.basename** 来处理,它会去除文件名之前的任何内容。

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

删除现有文件

如果您想删除任何现有文件,这非常简单。您只需编写以下代码:

def cleanup
   File.delete("#{RAILS_ROOT}/dirname/#{@filename}") 
   if File.exist?("#{RAILS_ROOT}/dirname/#{@filename}")
end

有关 **File** 对象的完整详细信息,您需要阅读我们的 **Ruby 参考手册**。

广告