MST

星途 面试题库

面试题:Ruby on Rails表单处理与文件上传

假设你正在开发一个Ruby on Rails应用,需要在表单中实现文件上传功能,上传的文件类型为图片。请阐述实现该功能的完整流程,包括在视图中如何创建文件上传表单,控制器如何接收和处理上传的文件,以及如何将文件保存到服务器指定目录,并确保文件安全性。
10.9万 热度难度
编程语言Ruby

知识考点

AI 面试

面试题答案

一键面试

1. 在视图中创建文件上传表单

在Rails视图文件(例如app/views/your_controller/new.html.erbedit.html.erb)中:

<%= form_with(model: your_model, local: true, multipart: true) do |form| %>
  <%= form.file_field :image %>
  <%= form.submit %>
<% end %>

这里model指定对应的模型,multipart: true是关键,用于支持文件上传。:image是自定义的字段名,对应模型中用于存储图片相关信息的属性。

2. 控制器接收和处理上传的文件

在对应的控制器(app/controllers/your_controller.rb)中:

class YourController < ApplicationController
  def create
    @your_model = YourModel.new(your_model_params)
    if @your_model.save
      redirect_to @your_model, notice: 'File uploaded successfully'
    else
      render :new
    end
  end

  private
  def your_model_params
    params.require(:your_model).permit(:image)
  end
end

your_model_params方法中,使用permit允许image字段通过强参数检查,create方法负责接收表单数据并尝试保存模型。

3. 将文件保存到服务器指定目录并确保文件安全性

首先,确保模型有对应的属性来关联文件。假设使用ActiveStorage(Rails内置文件管理系统):

  • 在模型中(app/models/your_model.rb)添加:
class YourModel < ApplicationRecord
  has_one_attached :image
end
  • 保存文件时,ActiveStorage会自动处理文件保存及安全性:
class YourController < ApplicationController
  def create
    @your_model = YourModel.new(your_model_params)
    if @your_model.save
      redirect_to @your_model, notice: 'File uploaded successfully'
    else
      render :new
    end
  end

  private
  def your_model_params
    params.require(:your_model).permit(:image)
  end
end

如果不使用ActiveStorage,可以手动保存文件:

  • 在控制器中:
class YourController < ApplicationController
  def create
    file = params[:your_model][:image]
    if file
      file_name = SecureRandom.uuid + '.' + file.original_filename.split('.').last
      file_path = Rails.root.join('public', 'uploads', file_name)
      File.open(file_path, 'wb') do |f|
        f.write(file.read)
      end
      @your_model = YourModel.new(your_model_params.merge(image_path: '/uploads/' + file_name))
      if @your_model.save
        redirect_to @your_model, notice: 'File uploaded successfully'
      else
        File.delete(file_path) if File.exist?(file_path)
        render :new
      end
    else
      @your_model = YourModel.new(your_model_params)
      if @your_model.save
        redirect_to @your_model, notice: 'File uploaded successfully'
      else
        render :new
      end
    end
  end

  private
  def your_model_params
    params.require(:your_model).permit
  end
end

这里使用SecureRandom.uuid生成唯一文件名,防止文件名冲突,并且保存文件到public/uploads目录。同时在保存失败时,删除已上传的文件以保证文件一致性。 并在模型中添加image_path字段来存储文件路径。

对于安全性,除了生成唯一文件名外:

  • 验证文件类型:
def validate_image_type
  return if image.blank?
  valid_types = ['image/jpeg', 'image/png', 'image/gif']
  errors.add(:image, 'is not a valid image type') unless valid_types.include?(image.content_type)
end

在模型中定义上述方法,并在validates中调用,确保上传的文件是图片类型。

  • 限制文件大小:
def validate_image_size
  return if image.blank?
  max_size = 5.megabytes
  errors.add(:image, 'is too large') if image.byte_size > max_size
end

同样在模型中定义并使用validates调用,防止过大文件上传。