Dateien mit Rails und Shrine hochladen

Es gibt viele Dateien zum Hochladen von Edelsteinen wie CarrierWave, Paperclip und Dragonfly, um nur einige zu nennen. Sie alle haben ihre Besonderheiten, und wahrscheinlich haben Sie bereits mindestens einen dieser Edelsteine ​​verwendet.

Heute möchte ich jedoch eine relativ neue, aber sehr coole Lösung namens Shrine vorstellen, die von Janko Marohnić erstellt wurde. Im Gegensatz zu einigen anderen ähnlichen Edelsteinen wird ein modularer Ansatz verfolgt, was bedeutet, dass jedes Feature als Modul (oder Plugin in der Terminologie von Shrine). Möchten Sie Validierungen unterstützen? Fügen Sie ein Plugin hinzu. Möchten Sie einige Dateien bearbeiten? Fügen Sie ein Plugin hinzu! Ich liebe diesen Ansatz sehr, da er auf einfache Weise kontrolliert, welche Funktionen für welches Modell verfügbar sind.

In diesem Artikel zeige ich Ihnen, wie Sie:

  • integrieren Sie den Schrein in eine Rails-Anwendung
  • konfigurieren Sie es (global und per Uploader)
  • Fügen Sie die Möglichkeit hinzu, Dateien hochzuladen
  • Prozessdateien
  • Validierungsregeln hinzufügen
  • Speichern Sie zusätzliche Metadaten und verwenden Sie File Cloud-Speicher mit Amazon S3

Der Quellcode für diesen Artikel ist auf GitHub verfügbar.

Die Arbeitsdemo finden Sie hier.

Heiligtum integrieren

Erstellen Sie zunächst eine neue Rails-Anwendung ohne die Standard-Testsuite:

Schienen neuer FileGuru -T

Ich werde Rails 5 für diese Demo verwenden, aber die meisten Konzepte gelten auch für die Versionen 3 und 4.

Lass den Schrein-Edelstein in dein Gemfile fallen:

Juwel "Schrein"

Dann renne:

Bundle installieren

Jetzt benötigen wir ein Modell, das ich anrufen werde Foto. Shrine speichert alle dateibezogenen Informationen in einer speziellen Textspalte, die mit a endet _Daten Suffix. Erstellen Sie die entsprechende Migration und wenden Sie sie an:

Schienen g Modell Fototitel: string image_data: Text Schienen db: migrieren

Beachten Sie, dass der letzte Befehl für ältere Versionen von Rails lauten sollte:

rake db: migrieren

Konfigurationsoptionen für Shrine können sowohl global als auch pro Modell festgelegt werden. Die globalen Einstellungen werden natürlich in der Initialisierungsdatei vorgenommen. Dort werde ich die notwendigen Dateien anschließen und Plugins. In Shrine werden Plugins verwendet, um Funktionsbestandteile in separate Module zu extrahieren. So haben Sie die volle Kontrolle über alle verfügbaren Funktionen. Es gibt beispielsweise Plugins für die Validierung, Bildverarbeitung, das Caching von Anhängen und mehr.

Lassen Sie uns zunächst zwei Plugins hinzufügen: eines zur Unterstützung von ActiveRecord und eines zur Einrichtung der Protokollierung. Sie werden global aufgenommen. Richten Sie außerdem den Dateisystemspeicher ein:

config / initializers / shrine.rb

erfordern "Schrein" erfordern "Schrein / Speicher / Dateisystem" Shrine.plugin: activerecord Shrine.plugin: Protokollierung, Protokollierung: Rails.logger Shrine.storages = cache: Shrine :: Storage :: FileSystem.new (Präfix "public") : "uploads / cache"), store: Shrine :: Storage :: FileSystem.new ("public", Präfix: "uploads / store"),

Logger gibt einfach einige Debugging-Informationen in der Konsole aus, um anzugeben, wie viel Zeit für die Verarbeitung einer Datei aufgewendet wurde. Das kann nützlich sein.

2015-10-09T20: 06: 06.676Z # 25602: STORE [Cache] ImageUploader [: avatar] Benutzer [29543] 1 Datei (0,1 s) 2015-10-09T20: 06: 06.854Z # 25602: PROCESS [store]: ImageUploader [: avatar] User [29543] 1-3 Dateien (0.22s) 2015-10-09T20: 06: 07.133Z # 25602: DELETE [zerstört]: ImageUploader [: avatar] User [29543] 3 Dateien (0.07s)

Alle hochgeladenen Dateien werden im gespeichert öffentlich / Uploads Verzeichnis. Ich möchte diese Dateien nicht in Git verfolgen, schließen Sie diesen Ordner aus:

.Gitignore

öffentlich / Uploads

Erstellen Sie nun eine spezielle "Uploader" -Klasse, in der modellspezifische Einstellungen gehostet werden. Im Moment wird diese Klasse leer sein:

models / image_uploader.rb

Klasse ImageUploader < Shrine end

Fügen Sie diese Klasse schließlich in die Foto Modell:

Modelle / Foto.rb

include ImageUploader [: image]

[:Bild] fügt ein virtuelles Attribut hinzu, das beim Erstellen eines Formulars verwendet wird. Die obige Zeile kann umgeschrieben werden als:

 Include ImageUploader.attachment (: image) # oder ImageUploader :: Attachment.new (: image) enthalten 

Nett! Jetzt ist das Modell mit Shrines Funktionalität ausgestattet und wir können mit dem nächsten Schritt fortfahren.

Controller, Ansichten und Routen

Für die Zwecke dieser Demo benötigen wir nur einen Controller zum Verwalten von Fotos. Das Index Seite wird als Wurzel dienen:

pages_controller.rb

Klasse PhotosController < ApplicationController def index @photos = Photo.all end end

Die Aussicht:

Ansichten / Fotos / index.html.erb

Fotos

<%= link_to 'Add Photo', new_photo_path %> <%= render @photos %>

Um das zu rendern @Fotos Array ist ein Teil erforderlich:

Ansichten / Fotos / _photo.html.erb

<% if photo.image_data? %> <%= image_tag photo.image_url %> <% end %>

<%= photo.title %> | <%= link_to 'Edit', edit_photo_path(photo) %>

Bilddaten? ist eine von ActiveRecord präsentierte Methode, die prüft, ob ein Datensatz ein Bild enthält.

Bild URL ist eine Shrine-Methode, die einfach einen Pfad zum Originalbild zurückgibt. Natürlich ist es viel besser, stattdessen ein kleines Miniaturbild anzuzeigen, aber wir werden uns später darum kümmern.

Fügen Sie alle erforderlichen Routen hinzu:

config / routes.rb

 Ressourcen: Nur Fotos: [: Neu,: Erstellen,: Index,: Bearbeiten,: Aktualisieren] Stamm 'Fotos # Index'

Das ist es - die Vorarbeit ist getan, und wir können mit dem interessanten Teil fortfahren!

Dateien hochladen

In diesem Abschnitt zeige ich Ihnen, wie Sie die Funktionalität hinzufügen, um Dateien tatsächlich hochzuladen. Die Controller-Aktionen sind sehr einfach:

photos_controller.rb

def new @photo = Photo.new Ende def create @photo = Photo.new (photo_params) if @ photo.save flash [: success] = 'Foto hinzugefügt! redirect_to photos_path else rendere 'new' end end

Das einzige Problem ist, dass Sie für starke Parameter das zulassen müssen Bild virtuelles Attribut, nicht das Bilddaten.

photos_controller.rb

private def photo_params params.require (: photo) .permit (: title,: image) ende

Erstellen Sie die Neu Aussicht:

Ansichten / Fotos / new.html.erb

Foto hinzufügen

<%= render 'form' %>

Das Teilformular des Formulars ist auch trivial:

Ansichten / Fotos / _form.html.erb

<%= form_for @photo do |f| %> <%= render "shared/errors", object: @photo %> <%= f.label :title %> <%= f.text_field :title %> <%= f.label :image %> <%= f.file_field :image %> <%= f.submit %> <% end %>

Beachten Sie erneut, dass wir das verwenden Bild Attribut, nicht das Bilddaten.

Fügen Sie schließlich einen weiteren Teil der Anzeigefehler hinzu:

views / shared / _errors.html.erb

<% if object.errors.any? %> 

Die folgenden Fehler wurden gefunden:

    <% object.errors.full_messages.each do |message| %>
  • <%= message %>
  • <% end %>
<% end %>

Das ist so ziemlich alles - Sie können jetzt Bilder hochladen.

Validierungen

Natürlich muss noch viel mehr getan werden, um die Demo-App abzuschließen. Das Hauptproblem ist, dass die Benutzer absolut jeden Dateityp mit beliebiger Größe hochladen können, was nicht besonders groß ist. Fügen Sie daher ein weiteres Plugin hinzu, um Validierungen zu unterstützen:

config / inititalizers / shrine.rb

Shrine.plugin: validation_helpers

Richten Sie die Validierungslogik für die ein ImageUploader:

models / image_uploader.rb

Attacher.validate do validate_max_size 1.megabyte, Nachricht: "ist zu groß (max. 1 MB)." Validate_mime_type_inclusion ['image / jpg', 'image / jpeg', 'image / png'] end

Ich darf nur JPG- und PNG-Bilder mit einer Größe von weniger als 1 MB hochladen. Passen Sie diese Regeln an, wenn Sie es für richtig halten.

MIME-Typen

Beachten Sie außerdem, dass Shrine standardmäßig den MIME-Typ einer Datei mithilfe des Content-Type-HTTP-Headers ermittelt. Dieser Header wird vom Browser übergeben und wird nur basierend auf der Dateierweiterung festgelegt. Dies ist nicht immer wünschenswert.

Wenn Sie den MIME-Typ basierend auf dem Inhalt der Datei ermitteln möchten, verwenden Sie ein Plugin mit dem Namen determin_mime_type. Ich werde es in die Uploader-Klasse aufnehmen, da andere Modelle diese Funktionalität möglicherweise nicht benötigen:

models / image_uploader.rb

Plugin: determin_mime_type

Dieses Plugin verwendet standardmäßig das Dateidienstprogramm von Linux.

Angehängte Bilder zwischenspeichern

Wenn ein Benutzer derzeit ein Formular mit falschen Daten sendet, wird das Formular mit den oben angegebenen Fehlern erneut angezeigt. Das Problem ist jedoch, dass das angehängte Bild verloren geht und der Benutzer es erneut auswählen muss. Dies ist sehr einfach zu beheben, indem ein weiteres Plugin namens cached_attachment_data verwendet wird:

models / image_uploader.rb

Plugin: cached_attachment_data

Fügen Sie nun einfach ein verstecktes Feld in Ihr Formular ein.

Ansichten / Fotos / _form.html.erb

<%= f.hidden_field :image, value: @photo.cached_image_data %> <%= f.label :image %> <%= f.file_field :image %>

Foto bearbeiten

Jetzt können Bilder hochgeladen werden, aber es gibt keine Möglichkeit, sie zu bearbeiten. Wir können sie also sofort korrigieren. Die entsprechenden Controller-Aktionen sind etwas trivial:

photos_controller.rb

def edit @photo = Photo.find (params [: id]) ende def update @photo = Photo.find (params [: id]) wenn @ photo.update_attributes (photo_params) flash [: success] = 'Foto bearbeitet!' redirect_to photos_path else rendere 'edit' end end

Das Gleiche _bilden teilweise wird verwendet:

Ansichten / Fotos / edit.html.erb

Foto bearbeiten

<%= render 'form' %>

Schön, aber nicht genug: Nutzer können ein hochgeladenes Bild immer noch nicht entfernen. Um dies zuzulassen, müssen wir raten, was-ein weiteres Plugin: 

models / image_uploader.rb

plugin: remove_attachment

Es verwendet ein virtuelles Attribut namens :entferne Bild, Erlaube es also im Controller:

photos_controller.rb

def photo_params params.require (: photo) .permit (: title,: image,: remove_image) ende

Zeigen Sie jetzt ein Kontrollkästchen an, um ein Bild zu entfernen, wenn ein Datensatz einen Anhang enthält:

Ansichten / Fotos / _form.html.erb

<% if @photo.image_data? %> Anlage entfernen: <%= f.check_box :remove_image %> <% end %>

Ein Miniaturbild erzeugen

Derzeit zeigen wir Originalbilder an, was für die Vorschau nicht der beste Weg ist: Fotos können groß sein und nehmen zu viel Platz ein. Natürlich können Sie einfach CSS verwenden Breite und Höhe Attribute, aber das ist auch eine schlechte Idee. Sie sehen, selbst wenn das Bild mit Stilen klein eingestellt ist, muss der Benutzer immer noch die Originaldatei herunterladen, die ziemlich groß sein kann.

Daher ist es viel besser, beim ersten Upload ein kleines Vorschaubild auf der Serverseite zu generieren. Dies beinhaltet zwei Plugins und zwei zusätzliche Edelsteine. Zuerst die Edelsteine ​​fallen lassen:

gem "image_processing" gem "mini_magick", "> = 4.3.5"

Image_processing ist ein besonderer Edelstein, der vom Autor von Shrine erstellt wurde. Es werden einige Hilfsmethoden auf hoher Ebene zum Bearbeiten von Bildern vorgestellt. Dieses Juwel wiederum basiert auf mini_magick, einem Ruby-Wrapper für ImageMagick. Wie Sie sich schon gedacht haben, benötigen Sie ImageMagick auf Ihrem System, um diese Demo auszuführen.

Installiere diese neuen Edelsteine:

Bundle installieren

Fügen Sie nun die Plugins mit ihren Abhängigkeiten hinzu:

models / image_uploader.rb

erfordern den ImageUploader der Klasse "image_processing / mini_magick" < Shrine include ImageProcessing::MiniMagick plugin :processing plugin :versions # other code… end

Die Verarbeitung ist das Plugin, mit dem wir ein Bild bearbeiten können (z. B. verkleinern, drehen, in ein anderes Format konvertieren usw.). Versionen wiederum erlauben es uns, ein Bild in verschiedenen Varianten zu haben. Für diese Demo werden zwei Versionen gespeichert: "Original" und "Daumen" (Größe geändert auf 300 x 300).

Hier ist der Code zum Verarbeiten eines Bildes und zum Speichern seiner zwei Versionen:

models / image_uploader.rb

Klasse ImageUploader < Shrine process(:store) do |io, context|  original: io, thumb: resize_to_limit!(io.download, 300, 300)  end end

resize_to_limit! ist eine vom image_processing gem bereitgestellte Methode. Es verkleinert einfach ein Bild auf 300 x 300 wenn es größer ist und nichts tut, wenn es kleiner ist. Darüber hinaus bleibt das ursprüngliche Seitenverhältnis erhalten.

Wenn Sie nun das Bild anzeigen, müssen Sie entweder nur das Bild angeben :Original oder :Daumen Argument an die Bild URL Methode:

Ansichten / Fotos / _photo.html.erb

<% if photo.image_data? %> <%= image_tag photo.image_url(:thumb) %> <% end %>

<%= photo.title %> | <%= link_to 'Edit', edit_photo_path(photo) %>

Das Gleiche kann innerhalb des Formulars gemacht werden:

Ansichten / Fotos / _form.html.erb

<% if @photo.image_data? %> <%= image_tag @photo.image_url(:thumb) %> Anlage entfernen: <%= f.check_box :remove_image %> <% end %>

Um die verarbeiteten Dateien nach dem Hochladen automatisch zu löschen, können Sie ein Plugin mit dem Namen delete_raw hinzufügen:

models / image_uploader.rb

Plugin: delete_raw

Metadaten des Bildes

Abgesehen von dem Rendern eines Bildes können Sie auch die Metadaten abrufen. Lassen Sie uns zum Beispiel die Größe des Originalfotos und den MIME-Typ anzeigen:

Ansichten / Fotos / _photo.html.erb

<% if photo.image_data? %> <%= image_tag photo.image_url(:thumb) %>

Größe <%= photo.image[:original].size %> Bytes
Mime Typ <%= photo.image[:original].mime_type %>

<% end %>

<%= photo.title %> | <%= link_to 'Edit', edit_photo_path(photo) %>

Was ist mit seinen Abmessungen? Leider werden sie nicht standardmäßig gespeichert. Dies ist jedoch mit einem Plugin namens store_dimensions möglich.

Abmessungen des Bildes

Das store_dimensions-Plugin ist auf den fastimage-Edelstein angewiesen. Schließen Sie ihn jetzt an:

Juwel "Fastimage"

Vergiss nicht zu laufen:

Bundle installieren

Nun fügen Sie einfach das Plugin hinzu:

models / image_uploader.rb

plugin: store_dimensions

Und zeigen Sie die Abmessungen mit der Breite und Höhe Methoden:

Ansichten / Fotos / _photo.html.erb

<% if photo.image_data? %> <%= image_tag photo.image_url(:thumb) %>

Größe <%= photo.image[:original].size %> Bytes
Mime Typ <%= photo.image[:original].mime_type %>
Maße <%= "#photo.image[:original].widthx#photo.image[:original].height" %>

<% end %>

<%= photo.title %> | <%= link_to 'Edit', edit_photo_path(photo) %>

Es gibt auch eine Maße verfügbare Methode, die ein Array mit Breite und Höhe zurückgibt (z. B., [500, 750]).

Umzug in die Cloud

Entwickler wählen häufig Cloud-Dienste zum Hosten hochgeladener Dateien, und Shrine bietet eine solche Möglichkeit. In diesem Abschnitt zeige ich Ihnen, wie Sie Dateien zu Amazon S3 hochladen.

Als ersten Schritt fügen Sie zwei weitere Edelsteine ​​in die Gemfile:

gem "aws-sdk", "~> 2.1" Gruppe: Entwicklung von "Dotenv-Schienen"

aws-sdk ist erforderlich, um mit dem SDK von S3 zu arbeiten, während dotenv-rail zum Verwalten von Umgebungsvariablen in der Entwicklung verwendet wird.

Bundle installieren

Bevor Sie fortfahren, sollten Sie ein Schlüsselpaar für den Zugriff auf S3 über die API erwerben. Melden Sie sich dazu bei Amazon Web Services Console an (oder melden Sie sich an) und navigieren Sie zu Sicherheitsnachweise> Benutzer. Erstellen Sie einen Benutzer mit Berechtigungen zum Bearbeiten von Dateien in S3. Hier ist die einfache Richtlinie, die vollen Zugriff auf S3 bietet:

"Version": "2016-11-14", "Statement": ["Effect": "Allow", "Action": "s3: *", "Resource": "*"]

Laden Sie das Schlüsselpaar des erstellten Benutzers herunter. Alternativ können Sie Root-Zugriffsschlüssel verwenden, aber ich stark entmutigen Sie machen das, weil es sehr unsicher ist.

Als Nächstes erstellen Sie einen S3-Bucket, um Ihre Dateien zu hosten, und fügen Sie im Stammverzeichnis des Projekts eine Datei hinzu, um Ihre Konfiguration zu hosten:

.env

S3_KEY = YOUR_KEY S3_SECRET = YOUR_SECRET S3_BUCKET = YOUR_BUCKET S3_REGION = YOUR_REGION

Niemals jemals aussetzen Diese Datei der Öffentlichkeit zugänglich machen und sicherstellen, dass Sie sie von Git ausschließen:

.Gitignore

.env

Ändern Sie nun die globale Konfiguration von Shrine und führen Sie einen neuen Speicher ein:

config / initializers / shrine.rb

erfordern "Schrein" erfordern "Schrein / Speicher / s3" s3_options = access_key_id: ENV ['S3_KEY'], secret_access_key: ENV ['S3_SECRET'], Region: ENV ['S3_REGION'], Bucket: ENV ['S3_BUCKET'] , Shrine.storages = cache: Shrine :: Storage :: FileSystem.new ("public", Präfix: "uploads / cache"), store: Shrine :: Storage :: S3.new (Präfix: "store", ** s3_options),

Das ist es! An den anderen Teilen der App müssen keine Änderungen vorgenommen werden, und Sie können den neuen Speicher sofort testen. Wenn Sie von S3 Fehler erhalten, die sich auf falsche Schlüssel beziehen, stellen Sie sicher, dass Sie den Schlüssel und das Geheimnis genau kopiert haben, ohne nachgestellte Leerzeichen und unsichtbare Sonderzeichen.

Fazit

Wir sind am Ende dieses Artikels angelangt. Hoffentlich fühlen Sie sich jetzt sehr zuversichtlich, Shrine zu verwenden und möchten es gerne in einem Ihrer Projekte einsetzen. Wir haben viele Funktionen dieses Edelsteins besprochen, aber es gibt noch mehr, wie die Möglichkeit, zusätzlichen Kontext zusammen mit Dateien und den direkten Upload-Mechanismus zu speichern. 

Durchsuchen Sie daher die Dokumentation von Shrine und die offizielle Website, die alle verfügbaren Plugins ausführlich beschreibt. Wenn Sie weitere Fragen zu diesem Schmuckstück haben, zögern Sie nicht, sie zu veröffentlichen. Ich danke dir, dass du bei mir bleibst und wir sehen uns bald!