Chargement avec Rails et Carrierwave

Ceci est un autre article de la série "Uploading with Rails". Aujourd'hui, nous allons rencontrer Carrierwave, l'une des solutions de téléchargement de fichiers les plus populaires pour Rails. J'aime Carrierwave car il est facile de démarrer, il offre de nombreuses fonctionnalités prêtes à l'emploi et fournit des dizaines d'articles "Comment faire" écrits par les membres de la communauté, pour que vous ne vous perdiez pas.

Dans cet article, vous apprendrez comment:

  • Intégrez Carrierwave dans votre application Rails
  • Ajouter des validations
  • Persistance des fichiers dans les demandes
  • Supprimer des fichiers
  • Générer des vignettes
  • Télécharger des fichiers depuis des emplacements distants
  • Introduire plusieurs téléchargements de fichiers
  • Ajouter un support pour le stockage en nuage

Le code source de cet article est disponible sur GitHub. Bonne lecture!

Poser les fondations

Comme toujours, commencez par créer une nouvelle application Rails:

nouveaux rails UploadingWithCarrierwave -T

Pour cette démonstration, j'utiliserai Rails 5.0.2. Veuillez noter que Carrierwave 1 ne prend en charge que Rails 4+ et Ruby 2. Si vous utilisez encore Rails 3, connectez Carrierwave version 0.11..

Pour voir Carrierwave en action, nous allons créer une application de blogging très simple avec une seule Poster modèle. Il aura les attributs principaux suivants:

  • Titre (chaîne)
  • corps (texte)
  • image (chaîne) -ce champ va contenir une image (le nom du fichier, pour être précis) jointe à la publication

Générez et appliquez une nouvelle migration:

rails g model Titre de l'article: corps de la chaîne: image de texte: chaîne rails db: migrer

Mettre en place des itinéraires:

config / routes.rb

resources: posts root to: 'posts # index'

Créez également un contrôleur très basique:

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

Maintenant, fabriquons le indice vue:

views / posts / index.html.erb

Des postes

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

Et le partiel correspondant:

visionnements / publications / _post.html.erb

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

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

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


Ici, j'utilise les rails tronquer méthode pour afficher uniquement les 150 premiers symboles de la poste. Avant de créer d’autres vues et une forme partielle, intégrons d’abord Carrierwave à l’application..

Intégration de Carrierwave

Déposez un nouveau joyau dans le Gemfile:

Gemfile

bijou 'carrierwave', '~> 1,0'

Courir:

installation groupée

Carrierwave stocke sa configuration à l'intérieur téléchargeurs qui sont inclus dans vos modèles. Pour générer un téléchargeur, utilisez la commande suivante:

rails générer uploader Image

Maintenant, à l'intérieur app / uploaders, vous trouverez un nouveau fichier appelé image_uploader.rb. Notez qu'il contient des commentaires utiles et des exemples, vous pouvez donc vous en servir pour commencer. Dans cette démonstration, nous utiliserons ActiveRecord, mais Carrierwave prend également en charge Mongoid, Sequel et DataMapper..

Ensuite, nous devons inclure ou monter cet uploader dans le modèle:

modèles / post.rb

mount_uploader: image, ImageUploader

Le téléchargeur a déjà des paramètres par défaut sains, mais il faut au moins choisir où les fichiers téléchargés seront stockés. Pour l'instant, utilisons le stockage de fichiers:

uploaders / image_uploader.rb

stockage: fichier

Par défaut, les fichiers seront placés à l'intérieur du public / uploads répertoire, il est donc préférable de l'exclure du système de contrôle de version:

.gitignore

public / uploads

Vous pouvez également modifier le store_dir méthode à l'intérieur de votre téléchargeur pour choisir un autre emplacement.

À ce stade, nous pouvons créer une nouvelle vue et un formulaire partiel pour commencer à télécharger des fichiers:

views / posts / new.html.erb

Ajouter un post

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

views / posts / _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 %>

Notez que le PostsController n'a pas besoin d'être modifié car nous avons déjà autorisé la image attribut.

Enfin, créez la vue d'édition:

views / posts / edit.html.erb

Modifier le post

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

C'est tout! Vous pouvez démarrer le serveur et essayer de créer une publication avec une image. Le problème est que cette image n'est visible nulle part, passons à la section suivante et ajoutons une page de démonstration.!

Affichage des images

Donc, la seule vue que nous n’ayons pas encore créée est spectacle. Ajoutez-le maintenant:

views / posts / 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) %>

Comme vous pouvez le constater, l’affichage d’une pièce jointe est très simple: il vous suffit de dire @ post.image.url récupérer l'URL d'une image. Pour obtenir un chemin d'accès au fichier, utilisez la commande chemin_courant méthode. Notez que Carrierwave fournit également un image? méthode pour nous permettre de vérifier si une pièce jointe est présente (la image la méthode elle-même ne reviendra jamais néant, même si le fichier n'est pas présent).

Maintenant, après avoir navigué vers un article, vous devriez voir une image, mais elle pourrait paraître trop grande: après tout, nous ne limitons pas les dimensions, où que ce soit. Bien sûr, nous aurions pu réduire l’image avec certaines règles CSS, mais il est bien préférable de générer une vignette après le téléchargement du fichier. Cela nécessite cependant quelques étapes supplémentaires.

Générer des vignettes

Pour recadrer et redimensionner les images, nous avons besoin d’un outil séparé. Out of the box Carrierwave prend en charge les gemmes RMagick et MiniMagick qui, à leur tour, sont utilisées pour manipuler des images à l'aide d'ImageMagick. ImageMagick est une solution à code source ouvert vous permettant d’éditer des images existantes et d’en générer de nouvelles. Avant de poursuivre, vous devez donc le télécharger et l’installer. Ensuite, vous êtes libre de choisir l'une des deux pierres précieuses. Je vais rester avec MiniMagick, car il est beaucoup plus facile à installer et il a un meilleur support: 

Gemfile

gem 'mini_magick'

Courir:

installation groupée

Ensuite, incluez MiniMagick dans votre téléchargeur:

uploaders / image_uploader.rb

inclut CarrierWave :: MiniMagick

Maintenant, nous devons simplement introduire une nouvelle version pour notre téléchargeur. Le concept de les versions (ou styles) est utilisé dans de nombreuses bibliothèques de téléchargement de fichiers; cela signifie simplement que des fichiers supplémentaires basés sur la pièce jointe d'origine seront créés avec, par exemple, des dimensions ou des formats différents. Introduisez une nouvelle version appelée pouce:

uploaders / image_uploader.rb

version: thumb do process resize_to_fill: [350, 350] end

Vous pouvez avoir autant de versions que vous le souhaitez et, qui plus est, des versions peuvent même être construites sur d'autres:

uploaders / image_uploader.rb

version: small_thumb, from_version:: thumb do processus resize_to_fill: [20, 20] end

Si vous avez déjà téléchargé des images, elles ne disposeront pas de vignettes. Ce n’est pas un problème, car vous pouvez les recréer à partir de la console Rails:

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

Enfin, affichez votre vignette avec un lien vers l'image d'origine:

views / posts / show.html.erb

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

Démarrez le serveur et observez le résultat!

Ajout de validations

Actuellement, notre téléchargement fonctionne, mais nous ne validons pas du tout la saisie de l'utilisateur, ce qui est bien sûr mauvais. Tant que nous voulons travailler uniquement avec des images, ajoutons les extensions .png, .jpg et .gif à la liste blanche:

uploaders / image_uploader.rb

def extension_whitelist% w (jpg jpeg gif png) fin

Vous pouvez également ajouter des contrôles de type de contenu en définissant une contenu_type_whitelist méthode:

uploaders / image_uploader.rb

def content_type_whitelist / image \ // end

Alternativement, il est possible de mettre en liste noire certains types de fichiers, par exemple des exécutables, en définissant content_type_blacklist méthode.

Hormis la vérification du type et de l'extension d'un fichier, imposons qu'il soit inférieur à 1 Mo. Pour ce faire, nous aurons besoin d'une gemme supplémentaire prenant en charge les validations de fichier pour ActiveModel:

Gemfile

gem 'file_validators'

Installez-le:

installation groupée

Présentez maintenant les validations souhaitées (notez que j’ajoute également des contrôles pour le Titre et corps les attributs):

modèles / post.rb

valide: titre, présence: vrai, longueur: minimum: 2 valide: corps, présence: vrai valide: image, taille_fichier: less_than: 1.megabytes

La prochaine chose à faire est d’ajouter les traductions I18n pour les messages d’erreur de Carrierwave:

config / locales / en.yml

en: errors: messages: carrierwave_processing_error: "Impossible de redimensionner l'image." carrierwave_integrity_error: "Pas une image." carrierwave_download_error: "Impossible de télécharger l'image." extension_whitelist_error: "Vous n'êtes pas autorisé à télécharger des fichiers% extension, types autorisés:% allowed_types" extension_blacklist_error: "Vous n'êtes pas autorisé à télécharger des fichiers% extension, types interdits:% prohib_types"

Actuellement, nous n'affichons aucune erreur de validation, créons donc un partiel partagé:

views / shared / _errors.html.erb

<% if object.errors.any? %> 

Quelques erreurs ont été trouvées:

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

Employer cette partielle dans le formulaire:

views / posts / _form.html.erb

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

Maintenant, essayez de télécharger des fichiers invalides et observez le résultat. Cela devrait fonctionner, mais si vous choisissez un fichier valide et ne remplissez pas le titre ou le corps, les vérifications échoueront tout de même et une erreur s'affichera. Cependant, le champ de fichier sera effacé et l'utilisateur devra choisir à nouveau l'image, ce qui n'est pas très pratique. Pour résoudre ce problème, nous devons ajouter un autre champ au formulaire..

Persistance des fichiers sur plusieurs demandes

La persistance de fichiers lors de la réaffichage de formulaires est en réalité assez simple. Tout ce que vous avez à faire est d'ajouter un nouveau champ caché et de le permettre à l'intérieur du contrôleur:

views / shared / _form.html.erb

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

posts_controller.rb

params.require (: post) .permit (: titre,: corps,: image,: image_cache)

Maintenant le image_cache sera rempli automatiquement et l'image ne sera pas perdue. Il peut également être utile d’afficher une vignette pour que l’utilisateur comprenne que l’image a été traitée avec succès: 

views / shared / _form.html.erb

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

Supprimer des images

Une autre caractéristique très courante est la possibilité de supprimer les fichiers joints lors de la modification d'un enregistrement. Avec Carrierwave, l’implémentation de cette fonctionnalité n’est pas un problème. Ajouter une nouvelle case à cocher au formulaire:

views / shared / _form.html.erb

<% if post.image? %> <%= image_tag post.image.thumb.url %> 
<%= label_tag :remove_image do %> Supprimer l'image <%= f.check_box :remove_image %> <% end %>
<% end %>

Et permettre la remove_image attribut:

posts_controller.rb

params.require (: post) .permit (: titre,: corps,: image,: remove_image,: image_cache)

C'est tout! Pour supprimer une image manuellement, utilisez la commande remove_image! méthode:

@ post.remove_image!

Téléchargement depuis un emplacement distant

Carrierwave fournit également une fonctionnalité très pratique prête à l'emploi: la possibilité de télécharger des fichiers à partir d'emplacements distants à l'aide de leur URL. Introduisons maintenant cette possibilité en ajoutant un nouveau champ et en autorisant l'attribut correspondant: 

views / shared / _form.html.erb

<%= f.text_field :remote_image_url %> Entrer l'URL d'une image

posts_controller.rb

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

À quel point cela est cool? Vous n'avez pas du tout besoin de faire de modifications et vous pouvez tester cette fonctionnalité immédiatement.!

Travailler avec plusieurs téléchargements

Supposons que nous voulions que notre message ait plusieurs pièces jointes disponibles. Avec la configuration actuelle, ce n’est pas possible, mais heureusement, Carrierwave prend également en charge un tel scénario. Pour implémenter cette fonctionnalité, vous devez ajouter un champ sérialisé (pour SQLite) ou un champ JSON (pour Postgres ou MySQL). Je préfère cette dernière option. Passons donc maintenant à un nouvel adaptateur de base de données. Retirez la pierre précieuse sqlite3 du Gemfile et ajoutez pg à la place:

Gemfile

gem 'pg'

Installez-le:

installation groupée

Modifiez la configuration de la base de données comme ceci:

config / database.yml

default: & adaptateur par défaut: postgresql pool: 5 timeout: 5000 development: <<: *default database: upload_carrier_dev username: 'YOUR_USER' password: 'YOUR_PASSWORD' host: localhost

Créez la base de données Postgres correspondante, puis générez et appliquez la migration:

rails g migration add_attachments_to_posts pièces jointes: json rails db: migrate

Si vous préférez vous en tenir à SQLite, suivez les instructions répertoriées dans la documentation de Carrierwave..

Maintenant montez les téléchargeurs (notez le pluriel!):

modèle / post.rb

mount_uploaders: pièces jointes, ImageUploader

J'utilise le même outil de téléchargement pour les pièces jointes, mais vous pouvez bien sûr en générer un nouveau avec une configuration différente.

Ajoutez le champ de fichier multiple à votre formulaire:

views / shared / _form.html.erb

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

Tant que le pièces jointes champ va contenir un tableau, il devrait être autorisé de la manière suivante:

posts_controller.rb

params.require (: post) .permit (: titre,: body,: image,: remove_image,: image_cache,: remote_image_url, pièces jointes: [])

Enfin, vous pouvez parcourir les pièces jointes de la publication et les afficher comme d'habitude:

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 %>

Notez que chaque pièce jointe aura une vignette telle que configurée dans notre ImageUploader. Agréable!

Utiliser le stockage en nuage

S'en tenir au stockage de fichiers n'est pas toujours pratique et / ou possible, car, par exemple, Heroku ne permet pas de stocker des fichiers personnalisés. Par conséquent, vous pourriez vous demander comment associer Carrierwave au stockage en nuage Amazon S3? Eh bien, c'est une tâche assez facile aussi. Carrierwave dépend du joyau de brouillard pour mettre en œuvre cette fonctionnalité:

Gemfile

bijou "brouillard-aws"

Installez-le:

installation groupée

Créons un initialiseur pour Carrierwave et configurons globalement le stockage dans le cloud:

config / initializers / carrierwave.rb

CarrierWave.configure do | config | config.fog_provider = 'fog / aws' config.fog_credentials = fournisseur: 'AWS', aws_access_key_id: ENV ['S3_KEY'], aws_secret_access_key: ENV ['S3_SECRET'], région: ENV '' ['S3_SECRET'],]. répertoire_fog = ENV ['S3_BUCKET'] fin

Il y a quelques autres options disponibles, qui peuvent être trouvées dans la documentation.

J'utilise la gem dotenv-rails pour définir les variables d'environnement de manière sécurisée, mais vous pouvez choisir une autre option. Cependant, assurez-vous que votre paire de clés S3 n'est pas disponible publiquement, car tout le monde peut télécharger n'importe quoi dans votre compartiment.!

Ensuite, remplacez le stockage: fichier aligner avec:

uploaders / image_uploader.rb

stockage: brouillard

Outre S3, Carrierwave prend en charge les téléchargements vers Google Storage et Rackspace. Ces services sont également faciles à configurer.

Conclusion

C'est pour aujourd'hui! Nous avons couvert toutes les fonctionnalités principales de Carrierwave et vous pouvez maintenant commencer à l’utiliser dans vos projets. Il a quelques options supplémentaires disponibles, alors parcourez la documentation.

Si vous êtes bloqué, n'hésitez pas à poster vos questions. En outre, il pourrait être utile de jeter un coup d’œil sur le wiki de Carrierwave, qui contient des articles utiles sur la procédure à suivre, qui répondent à de nombreuses questions courantes..

Je vous remercie donc de rester avec moi et bon codage!