Active Storage in Rails 7

Active Storage in Rails 7

Vorbei sind die Zeiten, in denen du externe Gems verwenden musstest, um Datei-Uploads in Rails zu handhaben. Heutzutage ist Active Storage in Rails integriert und unterstützt verschiedene Cloud-basierte Speicherdienste wie Amazon S3, Google Cloud Storage und Microsoft Azure. Und im typischen Rails-Stil ist die Konfiguration minimal.
Wenn du sehen möchtest, wie Active Storage in einer umfassenden Rails-Anwendung verwendet wird, schau dir unseren umfassenden Rails-Kurs an. Wenn du einfach nur wissen möchtest, wie du es in deiner eigenen Anwendung zum Laufen bringst, wird dieses Tutorial dich durch die Schritte führen.

Wir werden die Dinge so organisieren, dass im development hochgeladene Bilder auf der lokalen Festplatte gespeichert werden. Wenn die App jedoch im production auf einem Heroku-Server läuft, werden die hochgeladenen Bilder in einem Amazon S3-Bucket gespeichert.

Install Active Storage

  1. Der erste Schritt ist, Active Storage zu installieren, indem du Folgendes im Verzeichnis deiner Rails-Anwendung ausführst:
$ rails active_storage:install
$ bin/rails active_storage:install
Dies kopiert einfach eine Migrationsdatei in das Verzeichnis db/migrate.
Führe dann diese Migration aus:
$ rails db:migrate
$ bin/rails db:migrate
Du wirst sehen, dass zwei Datenbanktabellen erstellt werden: `active_storage_blobs` und `active_storage_attachments`.

Hinter den Kulissen: Eine polymorphe Verbindungstabelle

Obwohl du diese beiden neuen Datenbanktabellen nicht direkt verwenden wirst, ist es immer hilfreich, ein grundlegendes Verständnis dafür zu haben, was hinter den Kulissen passiert. Also öffne die generierte Migrationsdatei und schau mal hinein.

Die Tabelle `active_storage_blobs` ist ziemlich unkompliziert:
create_table :active_storage_blobs do |t|
  t.string   :key,        null: false
  t.string   :filename,   null: false
  t.string   :content_type
  t.text     :metadata
  t.string   :service_name, null: false
  t.bigint   :byte_size,  null: false
  t.string   :checksum,   null: false

  ...
end
Sie speichert alle Details hochgeladener Dateien, einschließlich deren Dateiname, Content-Type, Metadaten und anderer solcher Informationen. Der Schlüssel ist ein codierter Wert, der auf die Datei im Active Storage-Service verweist.

Die Tabelle `active_storage_attachments` ist viel interessanter:
create_table :active_storage_attachments do |t|
  t.string     :name,     null: false
  t.references :record,   null: false, polymorphic: true, index: false
  t.references :blob,     null: false

  ...
end
Jede Zeile dieser Tabelle verweist auf einen Blob (eine Zeile in der Tabelle active_storage_blobs) und verweist auch auf einen Datensatz. Mit anderen Worten, jede Zeile der Tabelle active_storage_attachments verknüpft einen blob und einen Datensatz.

Was besonders interessant an dieser Verbindungstabelle ist, dass es sich um einen speziellen Typ von Verbindungstabelle handelt: eine polymorphe Verbindungstabelle. Das lässt sich daran erkennen, dass die Option `polymorphic: true` zu dieser Zeile hinzugefügt wird:
t.references :record, null: false, polymorphic: true, index: false
Das bedeutet, dass der Datensatz, auf den verwiesen wird, jedes ActiveRecord-Modell sein kann.

Wenn die Option `polymorphic: true` hier nicht hinzugefügt worden wäre, würde die Migration einfach eine Spalte mit dem Namen `record_id` erstellen, die einen Fremdschlüssel enthält, der auf einen Datensatz verweist. Aber sie würde nicht wissen, auf welchen Typ von Datensatz sie zeigt! 😲

Durch Hinzufügen der Option `polymorphic: true` erstellt die Migration zwei Spalten: eine Spalte mit dem Namen `record_id`, die einen Fremdschlüssel enthält, und eine Spalte mit dem Namen `record_type`, die den Namen einer ActiveRecord-Modellklasse enthält. Hier ist beispielsweise eine polymorphe Verbindungstabelle, die ein Ereignis mit seinem hochgeladenen Bild und einen Benutzer mit seinem hochgeladenen Bild verknüpft:


active_storage.png 69.7 KB



Beachte, dass die erste Zeile in der Tabelle `active_storage_attachments` einen Fremdschlüssel auf das Ereignis und einen weiteren Fremdschlüssel auf den Blob zeigt. Sie weiß, dass `record_id` in diesem Fall ein Fremdschlüssel für ein Ereignis ist, weil die Spalte `record_type` den Wert "Event" enthält.

Die zweite Zeile in der Tabelle `active_storage_attachments` hat einen Fremdschlüssel auf den Benutzer (weil `User` in der Spalte `record_type` steht) und einen Fremdschlüssel auf den Blob.

Hier ist die Kernaussage: Die Tabelle active_storage_attachments verknüpft jedes ActiveRecord-Modell mit seinen Bildern (oder Anhängen), weil es sich um eine polymorphe Verbindungstabelle handelt. Mit diesen beiden Informationen - einem Fremdschlüssel und einem Klassennamen - weiß die Zeile genau auf welchen Datensatz sie verweist. 👌

Declare Attachment Associations

gem 'image_processing', '~> 1.2'
bundle install
Die Datenbank ist nun bereit, jedes ActiveRecord-Modell mit einem Blob (einem hochgeladenen Bild) zu verknüpfen, aber wir müssen immer noch Verknüpfungen in unseren Modellen deklarieren.

Suppose, for example, we want our Event model to have one attached image—the main image for the event. To do that, we add a declaration in the appropriate model like so:
has_one_attached :main_image
Der Name muss nicht dem Namen einer Datenbankspalte entsprechen. Du kannst die Anlage nach Belieben benennen. Hier haben wir sie beispielsweise `main_image` genannt. Wenn du zum Beispiel ein Avatar-Bild an ein Benutzermodell anhängen würdest, könntest du die Anlage `avatar` nennen.

Also, was bewirkt diese `has_one_attached`-Deklaration? Nun, mithilfe von etwas Metaprogrammierung erstellt sie effektiv zwei Verknüpfungen, ähnlich wie:
has_one :main_image_attachment, dependent: :destroy
has_one :main_image_blob, through: :main_image_attachment
Unser Modell hat also eine main_image_attachment und einen main_image_blob, auf die es über die Verknüpfung main_image_attachment zugreifen kann. Wenn du den Quellcode der Methode has_one_attached ansiehst, gibt es vielleicht noch ein wenig mehr dazu, aber grundsätzlich ist das die Funktionsweise. Es ist keine Magie!

Upload an Image

Das Modell ist jetzt bereit, einen Bildanhang zu akzeptieren. Um Benutzern die Auswahl einer Datei zum Hochladen von ihrem Computer zu ermöglichen, benötigen wir ein Formular, das ein Eingabefeld für den Dateiupload enthält, so etwa:
<%= f.label :main_image %>
<%= f.file_field :main_image %>
Stelle sicher, dass du ein file_field verwendest und das Attribut main_image (oder wie immer du es genannt hast) referenzierst. Außerdem, damit dies funktioniert, vergiss nicht, das Attribut main_image zur Liste der erlaubten Parameter im Controller, der das Formular bearbeitet, hinzuzufügen.

Jetzt können wir in unserer App das Formular verwenden, um ein Bild auszuwählen und hochzuladen!

Zeige ein hochgeladenes Bild an

Um das Bild anzuzeigen, das an ein Modell angehängt ist, müssen wir einfach das main_image-Attribut des Modells verwenden, das in unserem Fall ein Ereignis ist. Zum Beispiel:
<%= image_tag event.main_image %>

Füge Validierungen hinzu

Welche Art von Dateien können Benutzer hochladen? Nun, wie es derzeit aussieht, gibt es keine Einschränkungen hinsichtlich des Dateityps oder ihrer Größe. Es wäre leichtsinnig, keine grundlegenden Validierungen hinzuzufügen. Möglicherweise denkst du, dass Active Storage integrierte Validierungen enthält, aber das ist nicht der Fall. Glücklicherweise ist es überraschend einfach, benutzerdefinierte Validierungen zu schreiben.

Angenommen, wir möchten die Bildgröße auf 1 MB oder weniger begrenzen und nur JPEG- und PNG-Dateien akzeptieren.

  1. Um das zu tun, müssen wir im Modell eine benutzerdefinierte Validierungsmethode registrieren. Wir nennen sie acceptable_image:
validate :acceptable_image
  1. Dann müssen wir diese Methode definieren. Als Ausgangspunkt möchten wir, dass unsere Validierungen nur ausgeführt werden, wenn ein Hauptbild angehängt ist. Andernfalls, wenn keines angehängt ist, müssen die Validierungen nicht durchgeführt werden. Um zu überprüfen, ob ein Hauptbild angehängt ist, können wir die Methode attached? wie folgt verwenden:
def acceptable_image
  return unless main_image.attached?
end
  1. Als nächstes können wir herausfinden, ob das hochgeladene Bild zu groß ist (über 1 MB), indem wir das Attribut byte_size des Attributs main_image lesen:
def acceptable_image
  return unless main_image.attached?

  unless main_image.blob.byte_size <= 1.megabyte
    errors.add(:main_image, "is too big")
  end
end
In Fällen, in denen das Bild zu groß ist, fügen wir eine Validierungsfehlermeldung zum Attribut main_image mit der Meldung "ist zu groß" hinzu.

  1. Dann können wir überprüfen, ob das hochgeladene Bild eine JPEG- oder PNG-Datei ist, indem wir das Attribut content_type des Attributs main_image lesen:
def acceptable_image
  return unless main_image.attached?

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

  acceptable_types = ["image/jpeg", "image/png"]
  unless acceptable_types.include?(main_image.content_type)
    errors.add(:main_image, "must be a JPEG or PNG")
  end
end
Wenn der content_type nicht einer unserer akzeptierten Typen ist, fügen wir eine Validierungsfehlermeldung zum Attribut main_image mit der Meldung "muss ein JPEG oder PNG sein" hinzu.

Betrachte diese Validierungen als absolute Mindestanforderungen. Du solltest sorgfältig darüber nachdenken, welche Arten von Dateien in deiner App hochgeladen werden können und welche Größe du verarbeiten möchtest. Als Alternative zum Schreiben benutzerdefinierter Validierungen für Bilduploads kannst du dir das Active Storage Validations-Gem ansehen, das verschiedene häufig verwendete Validierungen enthält, die du durchführen möchtest.

Quellen: 

https://pragmaticstudio.com/tutorials/using-active-storage-in-rails
https://guides.rubyonrails.org/active_storage_overview.html

blob active_Storage:

https://gist.github.com/yshmarov/f4515ebf631d3cc6f1720b0a91dbc7e7?permalink_comment_id=3742541

trix-editor mit imageupload:

https://gorails.com/episodes/trix-editor
Meld dich an und schreibe ein Kommentar