Caricamento con Rails e Carrierwave

Questo è un altro articolo nella serie "Uploading with Rails". Oggi incontreremo Carrierwave, una delle soluzioni di upload di file più popolari per Rails. Mi piace Carrierwave perché è facile da iniziare, ha molte funzionalità pronte all'uso e fornisce dozzine di articoli "how to" scritti dai membri della community, quindi non ti perderai.

In questo articolo, imparerai come:

  • Integrare Carrierwave nell'app Rails
  • Aggiungi convalide
  • Persistere i file tra le richieste
  • Rimuovi i file
  • Genera miniature
  • Carica file da posizioni remote
  • Introdurre più caricamenti di file
  • Aggiungi il supporto per lo storage cloud

Il codice sorgente per questo articolo è disponibile su GitHub. Buona lettura!

Posa delle fondamenta

Come sempre, inizia creando una nuova applicazione Rails:

rota nuovo UploadingWithCarrierwave -T

Per questa demo userò Rails 5.0.2. Tieni presente che Carrierwave 1 supporta solo Rails 4+ e Ruby 2. Se stai ancora utilizzando Rails 3, collega la versione 0.1 di Carrierwave.

Per vedere Carrierwave in azione, creeremo un'applicazione di blogging molto semplice con una suola Inviare modello. Avrà i seguenti attributi principali:

  • titolo (stringa)
  • corpo (testo)
  • Immagine (stringa): questo campo contiene un'immagine (il nome di un file, per la precisione) collegato al post

Genera e applica una nuova migrazione:

rails g model Titolo del post: string body: text image: string rails db: migrate

Imposta alcuni percorsi:

config / routes.rb

risorse: pubblica la radice su: 'posts # index'

Inoltre, crea un controller di base:

posts_controller.rb

classe PostsController < ApplicationController before_action :set_post, only: [:show, :edit, :update] def index @posts = Post.order('created_at DESC') end def show end def new @post = Post.new end def create @post = Post.new(post_params) if @post.save redirect_to posts_path else render :new end end def edit end def update if @post.update_attributes(post_params) redirect_to post_path(@post) else render :edit end end private def post_params params.require(:post).permit(:title, :body, :image) end def set_post @post = Post.find(params[:id]) end end

Ora realizziamo il indice vista:

views / messaggi / index.html.erb

Messaggi

<%= link_to 'Add post', new_post_path %> <%= render @posts %>

E il corrispondente parziale:

views / messaggi / _post.html.erb

<%= link_to post.title, post_path(post) %>

<%= truncate(post.body, length: 150) %>

<%= link_to 'Edit', edit_post_path(post) %>


Qui sto usando i Rails troncare metodo per visualizzare solo i primi 150 simboli dal post. Prima di creare altre viste e un modulo parziale, in primo luogo integrare Carrierwave nell'applicazione.

Integrando Carrierwave

Inserisci una nuova gemma nel Gemfile:

Gemfile

gem 'carrierwave', '~> 1.0'

Correre:

installazione bundle

Carrierwave memorizza la sua configurazione all'interno uploaders che sono inclusi nei tuoi modelli. Per generare un uploader, utilizzare il seguente comando:

rails genera immagine uploader

Ora, dentro app / uploaders, troverai un nuovo file chiamato image_uploader.rb. Nota che ha alcuni commenti ed esempi utili, quindi puoi usarlo per iniziare. In questa demo useremo ActiveRecord, ma Carrierwave ha anche il supporto per Mongoid, Sequel e DataMapper.

Successivamente, dobbiamo includere o montare questo uploader nel modello:

modelli / post.rb

mount_uploader: image, ImageUploader

L'uploader ha già impostazioni predefinite, ma per lo meno dobbiamo scegliere dove archiviare i file caricati. Per ora, utilizziamo lo spazio per i file:

uploaders / image_uploader.rb

archiviazione: file

Per impostazione predefinita, i file verranno posizionati all'interno di pubblici / uploads directory, quindi è meglio escluderla dal sistema di controllo della versione:

.gitignore

pubblici / uploads

È inoltre possibile modificare il store_dir metodo all'interno del tuo uploader per scegliere un'altra posizione.

A questo punto, possiamo creare una nuova vista e un modulo parziale per iniziare a caricare i file:

views / messaggi / new.html.erb

Aggiungi post

<%= render 'form', post: @post %>

views / messaggi / _form.html.erb

<%= form_for post do |f| %> 
<%= f.label :title %> <%= f.text_field :title %>
<%= f.label :body %> <%= f.text_area :body %>
<%= f.label :image %> <%= f.file_field :image %>
<%= f.submit %> <% end %>

Si noti che il PostsController non ha bisogno di essere modificato in quanto abbiamo già permesso il Immagine attributo.

Infine, crea la vista di modifica:

views / messaggi / edit.html.erb

Modifica post

<%= render 'form', post: @post %>

Questo è tutto! Puoi avviare il server e provare a creare un post con un'immagine. Il problema è che questa immagine non è visibile da nessuna parte, quindi passiamo alla sezione successiva e aggiungiamo una pagina di presentazione!

Visualizzazione delle immagini

Quindi, l'unica vista che non abbiamo ancora creato è mostrare. Aggiungilo ora:

views / messaggi / show.html.erb

<%= link_to 'All posts', posts_path %> 

<%= @post.title %>

<%= image_tag(@post.image.url, alt: 'Image') if @post.image? %>

<%= @post.body %>

<%= link_to 'Edit', edit_post_path(@post) %>

Come puoi vedere, la visualizzazione di un allegato è davvero semplice: tutto quello che devi fare è dire @ post.image.url per prendere l'URL di un'immagine. Per ottenere un percorso per il file, utilizzare il current_path metodo. Si noti che Carrierwave fornisce anche un Immagine? metodo per noi per verificare se un allegato è presente a tutti (il Immagine il metodo stesso non tornerà mai più zero, anche se il file non è presente).

Ora, dopo aver navigato in un post, dovresti vedere un'immagine, ma potrebbe sembrare troppo grande: dopo tutto, non stiamo limitando le dimensioni da nessuna parte. Ovviamente, avremmo potuto ridimensionare l'immagine con alcune regole CSS, ma è molto meglio generare una miniatura dopo che il file è stato caricato. Questo, tuttavia, richiede alcuni passaggi aggiuntivi.

Generazione di miniature

Per ritagliare e ridimensionare le immagini, abbiamo bisogno di uno strumento separato. Out of the box Carrierwave supporta le gemme RMagick e MiniMagick che, a loro volta, vengono utilizzate per manipolare le immagini con l'aiuto di ImageMagick. ImageMagick è una soluzione open-source che consente di modificare le immagini esistenti e generarne di nuove, quindi prima di procedere è necessario scaricarlo e installarlo. Successivamente, sei libero di scegliere una delle due gemme. Continuerò con MiniMagick, perché è molto più facile da installare e ha un supporto migliore: 

Gemfile

gem 'mini_magick'

Correre:

installazione bundle

Quindi includi MiniMagick nel tuo uploader:

uploaders / image_uploader.rb

includere CarrierWave :: MiniMagick

Ora abbiamo semplicemente bisogno di introdurre una nuova versione per il nostro uploader. Il concetto di versioni (o stili) è usato in molte librerie di upload di file; significa semplicemente che verranno creati file aggiuntivi basati sull'allegato originale con, ad esempio, dimensioni o formati diversi. Presentare una nuova versione chiamata pollice:

uploaders / image_uploader.rb

versione: thumb process process resize_to_fill: [350, 350] end

Puoi avere tutte le versioni che vuoi e, per di più, le versioni possono anche essere costruite sopra altre:

uploaders / image_uploader.rb

versione: small_thumb, from_version:: thumb do process resize_to_fill: [20, 20] end

Se hai già caricato alcune immagini, non avranno miniature disponibili. Questo non è un problema, però, dato che puoi ricrearli dalla console di Rails:

rails c Post.find_each | post | post.image.recreate_versions! (: thumb) se post.immagine?

Infine, mostra la miniatura con un link all'immagine originale:

views / messaggi / show.html.erb

<%= link_to(image_tag(@post.image.thumb.url, alt: 'Image'), @post.image.url, target: '_blank') if @post.image? %> 

Avviare il server e osservare il risultato!

Aggiunta di convalide

Attualmente il nostro caricamento funziona, ma non stiamo affatto convalidando l'input dell'utente, il che, ovviamente, è sbagliato. Finché vogliamo lavorare solo con le immagini, aggiungiamo le estensioni .png, .jpg e .gif:

uploaders / image_uploader.rb

def extension_whitelist% w (jpg jpeg gif png) fine

È inoltre possibile aggiungere verifiche del tipo di contenuto definendo a content_type_whitelist metodo:

uploaders / image_uploader.rb

def content_type_whitelist / image \ // end

In alternativa, è possibile inserire in una lista nera alcuni tipi di file, ad esempio eseguibili, definendo il file content_type_blacklist metodo.

Oltre a controllare il tipo e l'estensione di un file, applichiamo un valore inferiore a 1 megabyte. Per farlo, avremo bisogno di un ulteriore gem che supporti le convalide dei file per ActiveModel:

Gemfile

gem 'file_validators'

Installalo:

installazione bundle

Ora introdurre le convalide desiderate (nota che aggiungo anche controlli per il titolo e corpo attributi):

modelli / post.rb

valida: title, presence: true, length: minimum: 2 validate: body, presence: true validates: image, file_size: less_than: 1.megabytes

La prossima cosa da fare è aggiungere le traduzioni I18n per i messaggi di errore di Carrierwave:

config / locales / en.yml

it: errors: messages: carrierwave_processing_error: "Can not resize image." carrierwave_integrity_error: "Not an image." carrierwave_download_error: "Impossibile scaricare l'immagine." extension_whitelist_error: "Non ti è permesso caricare file% extension, tipi permessi:% allowed_types" extension_blacklist_error: "Non ti è permesso caricare% extension file, tipi proibiti:% prohibited_types"

Al momento, non vengono visualizzati errori di convalida da nessuna parte, quindi creiamo un partial condiviso:

views / shared / _errors.html.erb

<% if object.errors.any? %> 

Alcuni errori sono stati trovati:

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

Impiega questo parziale all'interno del modulo:

views / messaggi / _form.html.erb

<%= render 'shared/errors', object: post %>

Ora prova a caricare alcuni file non validi e osserva il risultato. Dovrebbe funzionare, ma se si sceglie un file valido e non si compila il titolo o il corpo, i controlli continueranno a non riuscire e verrà visualizzato un errore. Tuttavia, il campo file verrà cancellato e l'utente dovrà scegliere di nuovo l'immagine, il che non è molto conveniente. Per risolvere il problema, è necessario aggiungere un altro campo al modulo.

File persistenti tra richieste

I file persistenti attraverso il ridisplay dei moduli sono in realtà abbastanza semplici. Tutto quello che devi fare è aggiungere un nuovo campo nascosto e permetterlo all'interno del controller:

views / shared / _form.html.erb

<%= f.label :image %> <%= f.file_field :image %>
<%= f.hidden_field :image_cache %>

posts_controller.rb

params.require (: post) .permit (: title,: body,: image,: image_cache)

Ora il image_cache verrà popolato automaticamente e l'immagine non andrà persa. Potrebbe essere utile visualizzare anche una miniatura in modo che l'utente comprenda che l'immagine è stata elaborata correttamente: 

views / shared / _form.html.erb

<% if post.image? %> <%= image_tag post.image.thumb.url %> <% end %>

Rimozione di immagini

Un'altra caratteristica molto comune è la possibilità di rimuovere i file allegati quando si modifica un record. Con Carrierwave, l'implementazione di questa funzione non è un problema. Aggiungi una nuova casella di controllo al modulo:

views / shared / _form.html.erb

<% if post.image? %> <%= image_tag post.image.thumb.url %> 
<%= label_tag :remove_image do %> Rimuovi immagine <%= f.check_box :remove_image %> <% end %>
<% end %>

E consentire il remove_image attributo:

posts_controller.rb

params.require (: post) .permit (: title,: body,: image,: remove_image,: image_cache)

Questo è tutto! Per rimuovere un'immagine manualmente, utilizzare il remove_image! metodo:

@ post.remove_image!

Caricamento da una posizione remota

Carrierwave fornisce anche una funzionalità molto interessante, la possibilità di caricare file da posizioni remote tramite il loro URL. Introduciamo questa abilità ora aggiungendo un nuovo campo e autorizzando l'attributo corrispondente: 

views / shared / _form.html.erb

<%= f.text_field :remote_image_url %> Inserisci l'URL per un'immagine

posts_controller.rb

params.require (: post) .permit (: title,: body,: image,: remove_image,: image_cache,: remote_image_url)

Quant'è fico? Non è necessario apportare alcuna modifica e puoi testare subito questa funzionalità!

Lavorare con più caricamenti

Supponiamo di volere che il nostro post abbia più allegati disponibili. Con l'attuale configurazione non è possibile, ma per fortuna, Carrierwave supporta anche questo scenario. Per implementare questa funzione, è necessario aggiungere un campo serializzato (per SQLite) o un campo JSON (per Postgres o MySQL). Preferisco la seconda opzione, quindi passiamo a un nuovo adattatore per database ora. Rimuovere la gemma sqlite3 dal Gemfile e aggiungi invece pg:

Gemfile

gemma 'pg'

Installalo:

installazione bundle

Modifica la configurazione del database in questo modo:

config / database.yml

default: & default adapter: postgresql pool: 5 timeout: 5000 development: <<: *default database: upload_carrier_dev username: 'YOUR_USER' password: 'YOUR_PASSWORD' host: localhost

Creare il database Postgres corrispondente, quindi generare e applicare la migrazione:

rails g migrazione add_attachments_to_posts allegati: json rails db: migrate

Se preferisci attenersi a SQLite, segui le istruzioni elencate nella documentazione di Carrierwave.

Ora monta gli uploader (nota il plurale!):

Modello / post.rb

mount_uploaders: allegati, ImageUploader

Sto utilizzando lo stesso uploader per gli allegati, ma ovviamente puoi crearne uno nuovo con una configurazione diversa.

Aggiungi il campo di più file al modulo:

views / shared / _form.html.erb

<%= f.label :attachments %> <%= f.file_field :attachments, multiple: true %>

Finché il allegati campo sta per contenere un array, dovrebbe essere permesso nel seguente modo:

posts_controller.rb

params.require (: post) .permit (: title,: body,: image,: remove_image,: image_cache,: remote_image_url, allegati: [])

Infine, puoi scorrere gli allegati del post e visualizzarli come al solito:

views / shared / show.html.erb

<% if @post.attachments? %> 
    <% @post.attachments.each do |attachment| %>
  • <%= link_to(image_tag(attachment.thumb.url, alt: 'Image'), attachment.url, target: '_blank') %>
  • <% end %>
<% end %>

Nota che ogni allegato avrà una miniatura come configurato nel nostro ImageUploader. Bello!

Utilizzo di Cloud Storage

Rimanere con la memorizzazione dei file non è sempre conveniente e / o possibile dato che, ad esempio, su Heroku non è possibile archiviare file personalizzati. Quindi potresti chiedere come sposare Carrierwave con lo storage cloud Amazon S3? Bene, questo è un compito abbastanza facile. Carrierwave dipende dalla gemma fog-aws per implementare questa funzione:

Gemfile

gemma "nebbia-aws"

Installalo:

installazione bundle

Creiamo un inizializzatore per Carrierwave e configuriamo lo storage cloud globalmente:

config / inizializzatori / carrierwave.rb

CarrierWave.configure do | config | config.fog_provider = 'nebbia / aws' config.fog_credentials = provider: 'AWS', aws_access_key_id: ENV ['S3_KEY'], aws_secret_access_key: ENV ['S3_SECRET'], regione: ENV ['S3_REGION'], config. fog_directory = ENV ['S3_BUCKET'] fine

Ci sono alcune altre opzioni disponibili, che possono essere trovate nella documentazione.

Sto usando la gemma dotenv-rails per impostare le variabili d'ambiente in modo sicuro, ma puoi scegliere qualsiasi altra opzione. Tuttavia, assicurati che la tua coppia di chiavi S3 non sia disponibile pubblicamente, altrimenti chiunque può caricare qualsiasi cosa nel tuo bucket!

Quindi, sostituire il archiviazione: file coincide:

uploaders / image_uploader.rb

stoccaggio: nebbia

Oltre a S3, Carrierwave supporta i caricamenti su Google Storage e Rackspace. Questi servizi sono facili da configurare.

Conclusione

Questo è per oggi! Abbiamo coperto tutte le principali funzionalità di Carrierwave e ora puoi iniziare a utilizzarlo nei tuoi progetti. Ha alcune opzioni aggiuntive disponibili, quindi consulta la documentazione.

Se sei bloccato, non esitate a postare le vostre domande. Inoltre, potrebbe essere utile dare uno sguardo al wiki di Carrierwave, che ospita utili articoli "come" che rispondono a molte domande comuni.

Quindi ti ringrazio per essere stato con me e felice codifica!