Как выполнять загрузку файлов в Rails?ruby-48

1. Active Storage

Active Storage появился в Rails 5.2 и стал стандартным способом работы с файлами.

Установка:

rails active_storage:install
rails db:migrate

Настройка хранилища :

local:
  service: Disk
  root: <%= Rails.root.join("storage") %>

amazon:
  service: S3
  access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %>
  secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %>
  region: us-east-1
  bucket: your-bucket

Использование в модели:

class User < ApplicationRecord
  has_one_attached :avatar
  has_many_attached :documents
end

Контроллер:

def update
  @user.avatar.attach(params[:avatar])
  redirect_to @user
end

Отображение файла:

<%= image_tag @user.avatar if @user.avatar.attached? %>

2. CarrierWave

Для более сложных сценариев обработки файлов.

Установка:

gem 'carrierwave'
bundle install
rails generate uploader Avatar

Модель:

class User < ApplicationRecord
  mount_uploader :avatar, AvatarUploader
end

Uploader :

class AvatarUploader < CarrierWave::Uploader::Base
  storage :file

  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

  version :thumb do
    process resize_to_fit: [100, 100]
  end
end

3. Прямая загрузка

Для больших файлов лучше использовать прямую загрузку в облачное хранилище.

Настройка Active Storage для прямых загрузок:

# config/environments/production.rb
config.active_storage.service = :amazon

Форма:

<%= form.file_field :avatar, direct_upload: true %>

JavaScript :

import { DirectUpload } from "@rails/activestorage"
const input = document.querySelector('input[type=file]')
input.addEventListener('change', (event) => {
  const file = event.target.files[0]
  const upload = new DirectUpload(file, '/rails/active_storage/direct_uploads')
  upload.create((error, blob) => {
    const hiddenField = document.createElement('input')
    hiddenField.setAttribute('type', 'hidden')
    hiddenField.setAttribute('value', blob.signed_id)
    hiddenField.name = input.name
    document.querySelector('form').appendChild(hiddenField)
  })
})

4. Валидация файлов

Для Active Storage:

class User < ApplicationRecord
  validate :acceptable_avatar

  def acceptable_avatar
    return unless avatar.attached?

    unless avatar.byte_size <= 1.megabyte
      errors.add(:avatar, "is too big")
    end

    acceptable_types = ["image/jpeg", "image/png"]
    unless acceptable_types.include?(avatar.content_type)
      errors.add(:avatar, "must be a JPEG or PNG")
    end
  end
end

Для CarrierWave:

class AvatarUploader < CarrierWave::Uploader::Base
  def extension_whitelist
    %w(jpg jpeg png)
  end

  def size_range
    0..1.megabytes
  end
end

5. Обработка изображений

Active Storage с миниатюрами:

# variant для преобразований
<%= image_tag user.avatar.variant(resize_to_limit: [100, 100]) %>

Использование ImageMagick:

gem 'image_processing'

6. Тестирование загрузки файлов

Системные тесты:

test "can upload file" do
  visit new_user_path
  attach_file('Avatar', Rails.root.join('test/fixtures/files/avatar.png'))
  click_button 'Create User'
  assert_text 'User was successfully created'
end

Модельные тесты:

test "validates avatar size" do
  user = users(:one)
  user.avatar.attach(io: File.open('big_image.jpg'), filename: 'big.jpg')
  assert_not user.valid?
end

7. Удаление файлов

Active Storage:

@user.avatar.purge  # Удалить файл
@user.avatar.detach # Отсоединить без удаления

CarrierWave:

@user.remove_avatar! # Удалить файл

Резюмируем: Rails предлагает несколько способов загрузки файлов - от стандартного Active Storage до специализированных гемов вроде CarrierWave. Выбор зависит от сложности требований: Active Storage отлично подходит для большинства задач, в то время как CarrierWave предлагает больше возможностей для кастомной обработки. Всегда валидируйте загружаемые файлы и учитывайте безопасность при работе с пользовательскими загрузками.