Ci sono molte gemme per il caricamento di file come CarrierWave, Paperclip e Dragonfly, per citarne alcuni. Hanno tutti le loro specifiche e probabilmente hai già usato almeno una di queste gemme.
Oggi, tuttavia, voglio presentare una soluzione relativamente nuova, ma molto interessante chiamata Shrine, creata da Janko Marohnić. In contrasto con altre gemme simili, ha un approccio modulare, il che significa che ogni funzionalità è impacchettata come modulo (o collegare nella terminologia di Shrine). Vuoi supportare le convalide? Aggiungi un plugin. Vuoi fare un po 'di elaborazione dei file? Aggiungi un plugin! Adoro questo approccio in quanto consente di controllare facilmente quali funzionalità saranno disponibili per quale modello.
In questo articolo ho intenzione di mostrarvi come:
Il codice sorgente per questo articolo è disponibile su GitHub.
La demo funzionante può essere trovata qui.
Per iniziare, crea una nuova applicazione Rails senza la suite di test predefinita:
rota nuovo FileGuru -T
Userò Rails 5 per questa demo, ma la maggior parte dei concetti si applica anche alle versioni 3 e 4.
Lascia cadere la gemma del Santuario nel tuo Gemfile:
gioiello "santuario"
Quindi esegui:
installazione bundle
Ora avremo bisogno di un modello che chiamerò Foto
. Shrine memorizza tutte le informazioni relative ai file in una colonna di testo speciale che termina con a _dati
suffisso. Crea e applica la migrazione corrispondente:
rails g model Titolo foto: string image_data: text rails db: migrate
Nota che per le versioni precedenti di Rails, quest'ultimo comando dovrebbe essere:
rake db: migrate
Le opzioni di configurazione per Shrine possono essere impostate sia globalmente che per modello. Le impostazioni globali sono, naturalmente, all'interno del file di inizializzazione. Lì ho intenzione di collegare i file necessari e plugins. I plugin vengono utilizzati in Shrine per estrarre parti di funzionalità in moduli separati, offrendo il pieno controllo di tutte le funzionalità disponibili. Ad esempio, esistono plug-in per la convalida, l'elaborazione delle immagini, gli allegati di memorizzazione nella cache e altro ancora.
Per ora, aggiungiamo due plugin: uno per supportare ActiveRecord e un altro per impostare la registrazione. Saranno inclusi a livello globale. Inoltre, imposta la memoria del file system:
richiedono "santuario" richiedono "santuario / storage / file_system" Shrine.plugin: activerecord Shrine.plugin: logging, logger: Rails.logger Shrine.storages = cache: Shrine :: Storage :: FileSystem.new ("public", prefisso : "uploads / cache"), archivia: Shrine :: Storage :: FileSystem.new ("public", prefisso: "uploads / store"),
Il logger semplicemente invierà alcune informazioni di debug all'interno della console per dirvi quanto tempo è stato impiegato per elaborare un file. Questo può tornare utile.
2015-10-09T20: 06: 06.676Z # 25602: STORE [cache] ImageUploader [: avatar] Utente [29543] 1 file (0.1s) 2015-10-09T20: 06: 06.854Z # 25602: PROCESSO [negozio]: ImageUploader [: avatar] Utente [29543] 1-3 file (0,22s) 2015-10-09T20: 06: 07.133Z # 25602: DELETE [distrutto]: ImageUploader [: avatar] Utente [29543] 3 file (0,07s)
Tutti i file caricati verranno memorizzati all'interno di pubblici / uploads directory. Non voglio tenere traccia di questi file in Git, quindi escludi questa cartella:
pubblici / uploads
Ora crea una speciale classe "uploader" che ospiterà le impostazioni specifiche del modello. Per ora, questo corso sarà vuoto:
classe ImageUploader < Shrine end
Infine, includi questa classe all'interno di Foto
modello:
include ImageUploader [: immagine]
[:Immagine]
aggiunge un attributo virtuale che verrà utilizzato durante la costruzione di un modulo. La riga sopra può essere riscritta come:
include ImageUploader.attachment (: image) # oppure includi ImageUploader :: Attachment.new (: image)
Bello! Ora il modello è dotato della funzionalità di Shrine e possiamo procedere al passaggio successivo.
Ai fini di questa demo, avremo bisogno di un solo controller per gestire le foto. Il indice
la pagina fungerà da radice:
PhotosController di classe < ApplicationController def index @photos = Photo.all end end
La vista:
Fotografie
<%= link_to 'Add Photo', new_photo_path %> <%= render @photos %>
Al fine di rendere il @fotografie
array, è richiesto un partial:
<% if photo.image_data? %> <%= image_tag photo.image_url %> <% end %><%= photo.title %> | <%= link_to 'Edit', edit_photo_path(photo) %>
image_data?
è un metodo presentato da ActiveRecord che controlla se un record ha un'immagine.
URL dell'immagine
è un metodo Shrine che restituisce semplicemente un percorso all'immagine originale. Certo, è molto meglio visualizzare una miniatura piccola, ma ci penseremo più tardi.
Aggiungi tutti i percorsi necessari:
risorse: foto, solo: [: nuovo,: crea,: indice,: modifica,: aggiornamento] root 'foto # indice'
Questo è quanto-il lavoro di base è fatto, e possiamo procedere alla parte interessante!
In questa sezione ti mostrerò come aggiungere la funzionalità per caricare effettivamente i file. Le azioni del controller sono molto semplici:
def new @photo = Photo.new end def create @photo = Photo.new (photo_params) se @ photo.save flash [: success] = 'Foto aggiunta!' redirect_to photos_path else render 'new' end end
L'unico risultato è che per i parametri forti devi permettere a Immagine
attributo virtuale, non il image_data
.
private def photo_params params.require (: photo) .permit (: title,: image) end
Crea il nuovo
vista:
Aggiungi foto
<%= render 'form' %>
Anche il partial del form è banale:
<%= 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 %>
Ancora una volta, nota che stiamo usando il Immagine
attributo, non il image_data
.
Infine, aggiungi un altro parziale per visualizzare gli errori:
<% if object.errors.any? %>Sono stati trovati i seguenti errori:
Questo è praticamente tutto: puoi iniziare a caricare immagini in questo momento.
Naturalmente, per completare l'app demo è necessario molto altro lavoro. Il problema principale è che gli utenti possono caricare assolutamente qualsiasi tipo di file con qualsiasi dimensione, il che non è particolarmente grande. Pertanto, aggiungi un altro plug-in per supportare le convalide:
Shrine.plugin: validation_helpers
Impostare la logica di convalida per il ImageUploader
:
Attacher.validate fai validate_max_size 1.megabyte, messaggio: "è troppo grande (max è 1 MB)" validate_mime_type_inclusion ['image / jpg', 'image / jpeg', 'image / png'] end
Sto permettendo di caricare solo immagini JPG e PNG inferiori a 1 MB. Modifica queste regole come meglio credi.
Un'altra cosa importante da notare è che, per impostazione predefinita, Shrine determinerà il tipo MIME di un file utilizzando l'intestazione HTTP Content-Type. Questa intestazione viene passata dal browser e impostata solo in base all'estensione del file, che non è sempre auspicabile.
Se si desidera determinare il tipo MIME in base al contenuto del file, quindi utilizzare un plug-in chiamato define_mime_type. Lo includerò nella classe uploader, poiché altri modelli potrebbero non richiedere questa funzionalità:
plugin: define_mime_type
Questo plugin utilizzerà l'utilità di file di Linux per impostazione predefinita.
Attualmente, quando un utente invia un modulo con dati errati, il modulo verrà visualizzato di nuovo con errori visualizzati sopra. Il problema, tuttavia, è che l'immagine allegata andrà persa e l'utente dovrà selezionarla ancora una volta. Questo è molto facile da risolvere usando un altro plugin chiamato cached_attachment_data:
plug-in: cached_attachment_data
Ora aggiungi semplicemente un campo nascosto nel tuo modulo.
<%= f.hidden_field :image, value: @photo.cached_image_data %> <%= f.label :image %> <%= f.file_field :image %>
Ora le immagini possono essere caricate, ma non c'è modo di modificarle, quindi risolviamola subito. Le azioni del controller corrispondente sono in qualche modo banali:
def edit @photo = Photo.find (params [: id]) end def update @photo = Photo.find (params [: id]) se @ photo.update_attributes (photo_params) flash [: success] = 'Foto modificata!' redirect_to photos_path else render 'modifica' end end
Lo stesso _modulo
parziale sarà utilizzato:
Modifica foto
<%= render 'form' %>
Bello, ma non abbastanza: gli utenti non riescono ancora a rimuovere un'immagine caricata. Per consentire ciò, abbiamo bisogno di indovinare un altro plugin:
plugin: remove_attachment
Usa un attributo virtuale chiamato : remove_image
, quindi permettilo all'interno del controller:
def photo_params params.require (: photo) .permit (: title,: image,: remove_image) end
Ora mostra solo una casella di controllo per rimuovere un'immagine se un record ha un allegato in atto:
<% if @photo.image_data? %> Rimuovi allegato: <%= f.check_box :remove_image %> <% end %>
Attualmente vengono visualizzate le immagini originali, che non rappresentano l'approccio migliore per le anteprime: le foto potrebbero essere grandi e occupare troppo spazio. Certo, potresti semplicemente utilizzare il CSS larghezza
e altezza
attributi, ma anche questa è una cattiva idea. Vedete, anche se l'immagine è impostata per essere piccola usando gli stili, l'utente dovrà comunque scaricare il file originale, che potrebbe essere abbastanza grande.
Pertanto, è molto meglio generare una piccola immagine di anteprima sul lato server durante il caricamento iniziale. Ciò include due plugin e due gemme aggiuntive. Innanzitutto, inserisci le gemme:
gem "image_processing" gem "mini_magick", "> = 4.3.5"
Image_processing è una gemma speciale creata dall'autore di Shrine. Presenta alcuni metodi di supporto di alto livello per manipolare le immagini. Questa gemma, a sua volta, si affida a mini_magick, un wrapper Ruby per ImageMagick. Come hai intuito, avrai bisogno di ImageMagick sul tuo sistema per eseguire questa demo.
Installa queste nuove gemme:
installazione bundle
Ora includi i plugin insieme alle loro dipendenze:
richiede ImageUploader di "image_processing / mini_magick" < Shrine include ImageProcessing::MiniMagick plugin :processing plugin :versions # other code… end
L'elaborazione è il plugin che ci consente di manipolare un'immagine (ad esempio, ridurla, ruotarla, convertirla in un altro formato, ecc.). Le versioni, a loro volta, ci permettono di avere un'immagine in diverse varianti. Per questa demo verranno memorizzate due versioni: "originale" e "thumb" (ridimensionate a 300x300
).
Ecco il codice per elaborare un'immagine e memorizzare le sue due versioni:
classe ImageUploader < Shrine process(:store) do |io, context| original: io, thumb: resize_to_limit!(io.download, 300, 300) end end
resize_to_limit!
è un metodo fornito dalla gemma image_processing. Riduce semplicemente un'immagine a 300x300
se è più grande e non fa nulla se è più piccolo. Inoltre, mantiene le proporzioni originali.
Ora quando visualizzi l'immagine, devi solo fornire il :originale
o :pollice
argomento al URL dell'immagine
metodo:
<% if photo.image_data? %> <%= image_tag photo.image_url(:thumb) %> <% end %><%= photo.title %> | <%= link_to 'Edit', edit_photo_path(photo) %>
Lo stesso può essere fatto all'interno del modulo:
<% if @photo.image_data? %> <%= image_tag @photo.image_url(:thumb) %> Rimuovi allegato: <%= f.check_box :remove_image %> <% end %>
Per eliminare automaticamente i file elaborati al termine del caricamento, puoi aggiungere un plugin chiamato delete_raw:
plugin: delete_raw
Oltre a rendere effettivamente un'immagine, puoi anche recuperare i suoi metadati. Vediamo ad esempio la dimensione della foto originale e il tipo MIME:
<% if photo.image_data? %> <%= image_tag photo.image_url(:thumb) %>Taglia <%= photo.image[:original].size %> byte
<% end %>
Tipo MIME <%= photo.image[:original].mime_type %>
<%= photo.title %> | <%= link_to 'Edit', edit_photo_path(photo) %>
E le sue dimensioni? Sfortunatamente, non sono memorizzati di default, ma questo è possibile con un plugin chiamato store_dimensions.
Il plugin store_dimensions si basa sulla gemma fastimage, quindi collegalo ora:
gemma "fastimage"
Non dimenticare di eseguire:
installazione bundle
Ora includi il plugin:
plugin: store_dimensions
E visualizza le dimensioni usando il larghezza
e altezza
metodi:
<% if photo.image_data? %> <%= image_tag photo.image_url(:thumb) %>Taglia <%= photo.image[:original].size %> byte
<% end %>
Tipo MIME <%= photo.image[:original].mime_type %>
Dimensioni <%= "#photo.image[:original].widthx#photo.image[:original].height" %><%= photo.title %> | <%= link_to 'Edit', edit_photo_path(photo) %>
Inoltre, c'è un dimensioni
metodo disponibile che restituisce un array contenente larghezza e altezza (ad esempio, [500, 750]
).
Gli sviluppatori spesso scelgono i servizi cloud per ospitare i file caricati e Shrine presenta questa possibilità. In questa sezione, ti mostrerò come caricare i file su Amazon S3.
Come primo passo, includi altre due gemme nel Gemfile:
gem "aws-sdk", "~> 2.1" group: sviluppo do gem 'dotenv-rails' fine
aws-sdk è richiesto per lavorare con l'SDK di S3, mentre le rotaie dotenv saranno usate per gestire le variabili di ambiente nello sviluppo.
installazione bundle
Prima di procedere, dovresti ottenere una coppia di chiavi per accedere a S3 tramite API. Per ottenerlo, accedi (o registrati) ad Amazon Web Services Console e naviga verso Credenziali di sicurezza> Utenti. Crea un utente con le autorizzazioni per manipolare i file su S3. Ecco la semplice politica che presenta l'accesso completo a S3:
"Versione": "14/11/2016", "Istruzione": ["Effetto": "Permetti", "Azione": "s3: *", "Risorsa": "*"]
Scarica la coppia di chiavi dell'utente creato. In alternativa, è possibile utilizzare le chiavi di accesso root, ma io scoraggiare fortemente tu dal farlo perché è molto insicuro.
Quindi, crea un bucket S3 per ospitare i tuoi file e aggiungi un file nella root del progetto per ospitare la tua configurazione:
S3_KEY = YOUR_KEY S3_SECRET = YOUR_SECRET S3_BUCKET = YOUR_BUCKET S3_REGION = YOUR_REGION
Mai e poi mai esporre questo file al pubblico e assicurati di escluderlo da Git:
.ENV
Ora modifica la configurazione globale di Shrine e introduci un nuovo spazio di archiviazione:
richiedono "shrine" richiedono "shrine / storage / s3" s3_options = access_key_id: ENV ['S3_KEY'], secret_access_key: ENV ['S3_SECRET'], regione: ENV ['S3_REGION'], bucket: ENV ['S3_BUCKET'] , Shrine.storages = cache: Shrine :: Storage :: FileSystem.new ("public", prefisso: "uploads / cache"), archivio: Shrine :: Storage :: S3.new (prefisso: "store", ** s3_options),
Questo è tutto! Non è necessario apportare modifiche alle altre parti dell'app e provare subito questo nuovo spazio di archiviazione. Se ricevi errori da S3 relativi a chiavi errate, assicurati di aver copiato accuratamente la chiave e il segreto, senza spazi finali e simboli speciali invisibili.
Siamo arrivati alla fine di questo articolo. Si spera che, ormai, tu ti senta molto fiducioso nell'usare il Santuario e sei desideroso di impiegarlo in uno dei tuoi progetti. Abbiamo discusso molte delle caratteristiche di questo gioiello, ma ce ne sono ancora di più, come la possibilità di archiviare il contesto aggiuntivo insieme ai file e il meccanismo di caricamento diretto.
Pertanto, consultare la documentazione di Shrine e il relativo sito Web ufficiale, che descrive in dettaglio tutti i plug-in disponibili. Se hai altre domande su questa gemma, non esitare a pubblicarle. Ti ringrazio per essere stato con me, e ci vedremo presto!