Active Storage in Rails 7
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.
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.
- 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:
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`.
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:
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:
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:
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:
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.
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:
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. 👌
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:
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:
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!
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!
Jetzt können wir in unserer App das Formular verwenden, um ein Bild auszuwählen und hochzuladen!
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 %>
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.
Angenommen, wir möchten die Bildgröße auf 1 MB oder weniger begrenzen und nur JPEG- und PNG-Dateien akzeptieren.
- Um das zu tun, müssen wir im Modell eine benutzerdefinierte Validierungsmethode registrieren. Wir nennen sie acceptable_image:
validate :acceptable_image
- 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
- 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.
- 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.
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
https://guides.rubyonrails.org/active_storage_overview.html