跳至内容 跳至搜索

Active Storage

Active Storage 简化了在云服务(如 Amazon S3Google Cloud StorageMicrosoft Azure Storage)中上传和引用文件,并将这些文件附加到 Active Records。支持使用一个主服务和其他服务中的镜像来实现冗余。它还提供了一个磁盘服务用于测试或本地部署,但重点是云存储。

文件可以从服务器上传到云,也可以直接从客户端上传到云。

此外,图像文件可以使用按需变体进行转换,以调整质量、纵横比、大小或任何其他 MiniMagickVips 支持的转换。

您可以在 Active Storage 概述 指南中了解更多关于 Active Storage 的信息。

与其他存储解决方案相比

Active Storage 的工作原理与 Rails 中其他附件解决方案相比的一个关键区别在于,它使用了内置的 BlobAttachment 模型(由 Active Record 支持)。这意味着现有的应用程序模型不需要修改,无需添加额外的列来关联文件。Active Storage 使用多态关联,通过 Attachment 连接模型,然后连接到实际的 Blob

Blob 模型存储附件元数据(文件名、内容类型等)以及它们在存储服务中的标识符键。Blob 模型不存储实际的二进制数据。它们旨在从精神上保持不变。一个文件,一个 Blob。您也可以将同一个 Blob 与多个应用程序模型关联。如果您想对给定的 Blob 进行转换,想法是您只需创建一个新的 Blob,而不是尝试修改现有的 Blob(当然,如果您不再需要以前版本的 Blob,您可以稍后将其删除)。

安装

运行 bin/rails active_storage:install 来复制 active_storage 迁移。

注意:如果找不到该任务,请确认 config/application.rb 中是否存在 require "active_storage/engine"

示例

一个附件

class User < ApplicationRecord
  # Associates an attachment and a blob. When the user is destroyed they are
  # purged by default (models destroyed, and resource files deleted).
  has_one_attached :avatar
end

# Attach an avatar to the user.
user.avatar.attach(io: File.open("/path/to/face.jpg"), filename: "face.jpg", content_type: "image/jpeg")

# Does the user have an avatar?
user.avatar.attached? # => true

# Synchronously destroy the avatar and actual resource files.
user.avatar.purge

# Destroy the associated models and actual resource files async, via Active Job.
user.avatar.purge_later

# Does the user have an avatar?
user.avatar.attached? # => false

# Generate a permanent URL for the blob that points to the application.
# Upon access, a redirect to the actual service endpoint is returned.
# This indirection decouples the public URL from the actual one, and
# allows for example mirroring attachments in different services for
# high-availability. The redirection has an HTTP expiration of 5 min.
url_for(user.avatar)

class AvatarsController < ApplicationController
  def update
    # params[:avatar] contains an ActionDispatch::Http::UploadedFile object
    Current.user.avatar.attach(params.require(:avatar))
    redirect_to Current.user
  end
end

多个附件

class Message < ApplicationRecord
  has_many_attached :images
end
<%= form_with model: @message, local: true do |form| %>
  <%= form.text_field :title, placeholder: "Title" %><br>
  <%= form.textarea :content %><br><br>

  <%= form.file_field :images, multiple: true %><br>
  <%= form.submit %>
<% end %>
class MessagesController < ApplicationController
  def index
    # Use the built-in with_attached_images scope to avoid N+1
    @messages = Message.all.with_attached_images
  end

  def create
    message = Message.create! params.expect(message: [ :title, :content, images: [] ])
    redirect_to message
  end

  def show
    @message = Message.find(params[:id])
  end
end

图像附件的变体

<%# Hitting the variant URL will lazy transform the original blob and then redirect to its new service location %>
<%= image_tag user.avatar.variant(resize_to_limit: [100, 100]) %>

File 服务策略

Active Storage 支持两种服务文件的方式:重定向和代理。

重定向

Active Storage 为文件生成稳定的应用程序 URL,当访问这些 URL 时,会重定向到签名的短期服务 URL。这使应用程序服务器免于承担服务文件数据的负担。这是默认的文件服务策略。

当应用程序配置为默认代理文件时,请使用 rails_storage_redirect_path_url 路由帮助器进行重定向。

<%= image_tag rails_storage_redirect_path(@user.avatar) %>

代理

可以选择代理文件。这意味着您的应用程序服务器将在响应请求时从存储服务下载文件数据。这对于从 CDN 提供文件很有用。

您可以配置 Active Storage 默认使用代理

# config/initializers/active_storage.rb
Rails.application.config.active_storage.resolve_model_to_route = :rails_storage_proxy

或者,如果您想明确地代理特定附件,您可以使用 rails_storage_proxy_pathrails_storage_proxy_url 形式的 URL 帮助器。

<%= image_tag rails_storage_proxy_path(@user.avatar) %>

直接上传

Active Storage 及其包含的 JavaScript 库支持直接从客户端上传到云。

直接上传安装

  1. 在应用程序的 JavaScript 包中包含 Active Storage JavaScript,或直接引用它。

    在应用程序 HTML 中直接引用,无需通过资产管道捆绑,并使用自动启动

    <%= javascript_include_tag "activestorage" %>
    

    在应用程序 HTML 中通过 importmap-rails 引用,无需通过资产管道捆绑,并使用 ESM 作为自动启动

    # config/importmap.rb
    pin "@rails/activestorage", to: "activestorage.esm.js"
    
    <script type="module-shim">
      import * as ActiveStorage from "@rails/activestorage"
      ActiveStorage.start()
    </script>
    

    使用资产管道

    //= require activestorage
    

    使用 npm 包

    import * as ActiveStorage from "@rails/activestorage"
    ActiveStorage.start()
    
  2. 使用直接上传 URL 对文件输入进行注释。

    <%= form.file_field :attachments, multiple: true, direct_upload: true %>
    
  3. 就是这样!在提交表单后,上传开始。

直接上传 JavaScript 事件

事件名称 事件目标 事件数据('event.detail`) 描述
‘direct-uploads:start` ‘<form>` 提交了包含用于直接上传字段的文件的表单。
‘direct-upload:initialize` ‘<input>` ‘{id, file}` 在提交表单后,为每个文件调度。
‘direct-upload:start` ‘<input>` ‘{id, file}` 直接上传正在开始。
‘direct-upload:before-blob-request` ‘<input>` ‘{id, file, xhr}` 在向应用程序发送请求以获取直接上传元数据之前。
‘direct-upload:before-storage-request` ‘<input>` ‘{id, file, xhr}` 在发送请求以存储文件之前。
‘direct-upload:progress` ‘<input>` ‘{id, file, progress}` 存储文件的请求正在进行中。
‘direct-upload:error` ‘<input>` ‘{id, file, error}` 发生错误。除非取消此事件,否则会显示‘alert`。
‘direct-upload:end` ‘<input>` ‘{id, file}` 直接上传已结束。
‘direct-uploads:end` ‘<form>` 所有直接上传均已结束。

许可证

Active Storage 在 MIT 许可证 下发布。

支持

API 文档位于

有关 Ruby on Rails 项目的错误报告可以在这里提交

功能请求应在 rails-core 邮件列表中进行讨论,请访问