跳至内容 跳至搜索

Active Storage

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

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

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

您可以在 Active Storage 概述 指南中详细了解 Active Storage。

与其他存储解决方案相比

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

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

安装

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

注意:如果找不到任务,请验证 require "active_storage/engine" 是否存在于 config/application.rb 中。

示例

一个附件

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

Variation 图像附件

<%# 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 邮件列表中讨论,地址如下

命名空间
方法
G
V

类公共方法

gem_version()

返回当前加载的 Active Storage 版本,以 Gem::Version 格式。

# File activestorage/lib/active_storage/gem_version.rb, line 5
def self.gem_version
  Gem::Version.new VERSION::STRING
end

version()

返回当前加载的 Active Storage 版本,以 Gem::Version 格式。

# File activestorage/lib/active_storage/version.rb, line 7
def self.version
  gem_version
end