Téléchargement de fichiers avec des rails et un sanctuaire

Il existe de nombreuses gemmes de téléchargement de fichiers telles que CarrierWave, Paperclip et Dragonfly, pour en nommer quelques-unes. Ils ont tous leurs spécificités et vous avez probablement déjà utilisé au moins un de ces joyaux.

Aujourd'hui, cependant, je souhaite présenter une solution relativement nouvelle mais très cool appelée Shrine, créée par Janko Marohnić. Contrairement à certains autres joyaux similaires, il a une approche modulaire, ce qui signifie que chaque fonctionnalité est compressée sous forme de module (ou brancher dans la terminologie du sanctuaire). Voulez-vous supporter les validations? Ajouter un plugin. Vous souhaitez effectuer un traitement de fichier? Ajouter un plugin! J'aime beaucoup cette approche car elle vous permet de contrôler facilement les fonctionnalités disponibles pour chaque modèle..

Dans cet article, je vais vous montrer comment:

  • intégrer Shrine dans une application Rails
  • configurez-le (globalement et par uploader)
  • ajouter la possibilité de télécharger des fichiers
  • traiter les fichiers
  • ajouter des règles de validation
  • stocker des métadonnées supplémentaires et utiliser le stockage en nuage de fichiers avec Amazon S3

Le code source de cet article est disponible sur GitHub.

La démo de travail peut être trouvé ici.

Sanctuaire Intégrant

Pour commencer, créez une nouvelle application Rails sans la suite de tests par défaut:

rails nouveau FileGuru -T

J'utiliserai Rails 5 pour cette démonstration, mais la plupart des concepts s'appliquent également aux versions 3 et 4.

Déposez la gemme du sanctuaire dans votre Gemfile:

joyau "sanctuaire"

Puis lancez:

installation groupée

Maintenant, nous aurons besoin d'un modèle que je vais appeler Photo. Le sanctuaire stocke toutes les informations relatives aux fichiers dans une colonne de texte spéciale se terminant par un. _Les données suffixe. Créez et appliquez la migration correspondante:

rails g model Titre de la photo: string image_data: texte rails db: migrate

Notez que pour les anciennes versions de Rails, la dernière commande devrait être:

rake db: migrer

Les options de configuration pour Shrine peuvent être définies globalement et par modèle. Les paramètres globaux sont bien sûr définis dans le fichier d'initialisation. Là je vais brancher les fichiers nécessaires et plugins. Les plugins sont utilisés dans Shrine pour extraire des morceaux de fonctionnalité dans des modules séparés, vous donnant ainsi le contrôle total de toutes les fonctionnalités disponibles. Par exemple, il existe des plugins pour la validation, le traitement des images, la mise en cache des pièces jointes, etc..

Pour l'instant, ajoutons deux plugins: un pour prendre en charge ActiveRecord et un autre pour configurer la journalisation. Ils vont être inclus globalement. En outre, configurez le stockage du système de fichiers:

config / initializers / shrine.rb

nécessite "sanctuaire" nécessite "sanctuaire / stockage / système_fichier" Shrine.plugin: activerecord Shrine.plugin: enregistrement, enregistreur: Rails.logger Shrine.storages = cache: Shrine :: Storage :: FileSystem.new ("public", préfixe : "uploads / cache"), magasin: Shrine :: Storage :: FileSystem.new ("public", préfixe: "uploads / store"),

Logger va simplement sortir des informations de débogage dans la console pour vous dire combien de temps a été passé pour traiter un fichier. Cela peut être utile.

2015-10-09T20: 06: 06.676Z # 25602: STORE [cache] ImageUploader [: avatar] Utilisateur [29543] 1 fichier (0.1s) 2015-10-09T20: 06: 06.854Z # 25602: PROCESS [store]: ImageUploader [: avatar] Utilisateur [29543] 1-3 fichiers (0.22s) 2015-10-09T20: 06: 07.133Z # 25602: SUPPRIMER [détruit]: ImageUploader [: avatar] Utilisateur [29543] 3 fichiers (0.07s)

Tous les fichiers téléchargés seront stockés à l'intérieur du public / uploads annuaire. Je ne veux pas suivre ces fichiers dans Git, alors excluez ce dossier:

.gitignore

public / uploads

Créez maintenant une classe spéciale "uploader" qui hébergera les paramètres spécifiques au modèle. Pour l'instant, cette classe va être vide:

models / image_uploader.rb

classe ImageUploader < Shrine end

Enfin, incluez cette classe dans le Photo modèle:

modèles / photo.rb

inclure ImageUploader [: image]

[:image] ajoute un attribut virtuel qui sera utilisé lors de la construction d'un formulaire. La ligne ci-dessus peut être réécrite comme suit:

 inclure ImageUploader.attachment (: image) # ou inclure ImageUploader :: Attachment.new (: image) 

Agréable! Maintenant, le modèle est équipé de la fonctionnalité de Shrine, et nous pouvons passer à l'étape suivante..

Contrôleur, vues et itinéraires

Pour les besoins de cette démonstration, un seul contrôleur suffit pour gérer les photos. le indice Cette page servira de racine:

pages_controller.rb

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

La vue:

views / photos / index.html.erb

Photos

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

Pour rendre le @Photos tableau, un partiel est requis:

vues / photos / _photo.html.erb

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

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

image_data? est une méthode présentée par ActiveRecord qui vérifie si un enregistrement a une image.

URL de l'image est une méthode Shrine qui renvoie simplement un chemin d'accès à l'image d'origine. Bien sûr, il vaut mieux afficher une petite vignette à la place, mais nous nous en occuperons plus tard..

Ajoutez tous les itinéraires nécessaires:

config / routes.rb

 ressources: photos, uniquement: [: nouveau,: créer,: index,: éditer,: mettre à jour] root 'photos # index'

C'est tout, le travail préparatoire est fait et nous pouvons passer à la partie intéressante!

Téléchargement de fichiers

Dans cette section, je vais vous montrer comment ajouter la fonctionnalité permettant de télécharger des fichiers. Les actions du contrôleur sont très simples:

photos_controller.rb

def new @photo = Photo.new et def def create @photo = Photo.new (photo_params) si @ photo.save flash [: success] = 'Photo ajoutée!' redirect_to photos_path sinon rendre 'new' fin

Le seul problème est que, pour les paramètres forts, vous devez autoriser la image attribut virtuel, pas le image_data.

photos_controller.rb

private def photo_params params.require (: photo) .permit (: titre,: image) fin

Créer le Nouveau vue:

views / photos / new.html.erb

Ajouter une photo

<%= render 'form' %>

La forme du formulaire est également triviale:

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

Encore une fois, notez que nous utilisons le image attribut, pas le image_data.

Enfin, ajoutez un autre partiel pour afficher les erreurs:

views / shared / _errors.html.erb

<% if object.errors.any? %> 

Les erreurs suivantes ont été trouvées:

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

C'est à peu près tout, vous pouvez commencer à télécharger des images dès maintenant.

Validations

Bien entendu, il reste encore beaucoup à faire pour compléter l'application de démonstration. Le principal problème est que les utilisateurs peuvent télécharger absolument tout type de fichier, quelle que soit leur taille, ce qui n’est pas particulièrement intéressant. Par conséquent, ajoutez un autre plugin pour prendre en charge les validations:

config / inititalizers / shrine.rb

Shrine.plugin: validation_helpers

Configurez la logique de validation pour le ImageUploader:

models / image_uploader.rb

Attacher.validate do validate_max_size 1.megabyte, message: "est trop volumineux (max est de 1 Mo)" validate_mime_type_inclusion ['image / jpg', 'image / jpeg', 'image / png'] end

J'autorise le téléchargement d'images JPG et PNG inférieures à 1 Mo. Modifiez ces règles comme bon vous semble.

Types MIME

Une autre chose importante à noter est que, par défaut, Shrine détermine le type MIME d'un fichier à l'aide de l'en-tête HTTP Content-Type. Cet en-tête est transmis par le navigateur et défini uniquement en fonction de l'extension du fichier, ce qui n'est pas toujours souhaitable..

Si vous souhaitez déterminer le type MIME en fonction du contenu du fichier, utilisez un plugin appelé Determ_mime_type. Je l'inclurai dans la classe de téléverseur, car les autres modèles pourraient ne pas nécessiter cette fonctionnalité:

models / image_uploader.rb

plugin: Determine_mime_type

Ce plugin utilisera l'utilitaire de fichier de Linux par défaut.

Mise en cache des images attachées

Actuellement, lorsqu'un utilisateur envoie un formulaire avec des données incorrectes, le formulaire sera affiché à nouveau avec les erreurs indiquées ci-dessus. Le problème, cependant, est que l'image attachée sera perdue et que l'utilisateur devra la sélectionner à nouveau. C'est très facile à corriger en utilisant un autre plugin appelé cached_attachment_data:

models / image_uploader.rb

plugin: cached_attachment_data

Maintenant, ajoutez simplement un champ caché dans votre formulaire.

views / photos / _form.html.erb

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

Éditer une photo

Les images peuvent maintenant être téléchargées, mais il n’ya aucun moyen de les éditer, corrigeons-les immédiatement. Les actions du contrôleur correspondant sont quelque peu triviales:

photos_controller.rb

def edit @photo = Photo.find (params [: id]) fin def update @photo = Photo.find (params [: id]) si @ photo.update_attributes (photo_params) flash [: success] = 'Photo modifiée!' redirect_to photos_path sinon rendre 'edit' fin fin

Le même _forme partiel sera utilisé:

views / photos / edit.html.erb

Modifier photo

<%= render 'form' %>

Sympa, mais pas suffisant: les utilisateurs ne peuvent toujours pas supprimer une image téléchargée. Afin de permettre cela, nous aurons besoin de deviner quel autre plugin: 

models / image_uploader.rb

plugin: remove_attachment

Il utilise un attribut virtuel appelé : remove_image, alors permettez-le à l'intérieur du contrôleur:

photos_controller.rb

def photo_params params.require (: photo) .permit (: titre,: image,: remove_image) fin

Maintenant, affichez simplement une case à cocher pour supprimer une image si un enregistrement a une pièce jointe en place:

views / photos / _form.html.erb

<% if @photo.image_data? %> Supprimer la pièce jointe: <%= f.check_box :remove_image %> <% end %>

Générer une image miniature

Actuellement, nous affichons des images originales, ce qui n’est pas la meilleure approche pour les aperçus: les photos peuvent être volumineuses et occuper trop d’espace. Bien sûr, vous pouvez simplement utiliser le CSS largeur et la taille attributs, mais c'est une mauvaise idée aussi. Vous voyez, même si l'image est définie pour être petite à l'aide de styles, l'utilisateur devra tout de même télécharger le fichier d'origine, ce qui pourrait être assez gros.

Par conséquent, il est bien préférable de générer une petite image d’aperçu côté serveur lors du téléchargement initial. Cela implique deux plugins et deux gemmes supplémentaires. Tout d'abord, déposez les pierres précieuses:

gem "traitement_image" gem "mini_magick", "> = 4.3.5"

Image_processing est un joyau spécial créé par l'auteur de Shrine. Il présente des méthodes d'assistance de haut niveau pour manipuler des images. Ce bijou, à son tour, repose sur mini_magick, un wrapper Ruby pour ImageMagick. Comme vous l'avez deviné, vous aurez besoin d'ImageMagick sur votre système pour pouvoir exécuter cette démonstration..

Installez ces nouveaux joyaux:

installation groupée

Incluez maintenant les plugins avec leurs dépendances:

models / image_uploader.rb

nécessite la classe "image_processing / mini_magick" ImageUploader < Shrine include ImageProcessing::MiniMagick plugin :processing plugin :versions # other code… end

Le traitement est le plug-in qui nous permet de manipuler une image (par exemple, la réduire, la faire pivoter, la convertir dans un autre format, etc.). Les versions, à leur tour, nous permettent d’avoir une image dans différentes variantes. Pour cette démonstration, deux versions seront stockées: "original" et "thumb" (redimensionné 300x300).

Voici le code pour traiter une image et stocker ses deux versions:

models / image_uploader.rb

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

resize_to_limit! est une méthode fournie par le gem image_processing. Il réduit simplement une image à 300x300 s'il est plus grand et ne fait rien s'il est plus petit. De plus, il conserve le format d'image original.

Maintenant, lors de l'affichage de l'image, il vous suffit de fournir soit le :original ou :pouce argument à la URL de l'image méthode:

vues / photos / _photo.html.erb

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

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

La même chose peut être faite à l'intérieur du formulaire:

views / photos / _form.html.erb

<% if @photo.image_data? %> <%= image_tag @photo.image_url(:thumb) %> Supprimer la pièce jointe: <%= f.check_box :remove_image %> <% end %>

Pour supprimer automatiquement les fichiers traités une fois le téléchargement terminé, vous pouvez ajouter un plugin appelé delete_raw:

models / image_uploader.rb

plugin: delete_raw

Métadonnées de l'image

Outre le rendu d'une image, vous pouvez également récupérer ses métadonnées. Par exemple, affichons la taille et le type MIME de la photo d'origine:

vues / photos / _photo.html.erb

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

Taille <%= photo.image[:original].size %> octets
Type MIME <%= photo.image[:original].mime_type %>

<% end %>

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

Qu'en est-il de ses dimensions? Malheureusement, ils ne sont pas stockés par défaut, mais cela est possible avec un plugin appelé store_dimensions.

Dimensions de l'image

Le plugin store_dimensions s'appuie sur la gemme fastimage, alors connectez-le maintenant:

gem 'fastimage'

N'oubliez pas de courir:

installation groupée

Maintenant, incluez simplement le plugin:

models / image_uploader.rb

plugin: store_dimensions

Et affichez les dimensions en utilisant le largeur et la taille méthodes:

vues / photos / _photo.html.erb

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

Taille <%= photo.image[:original].size %> octets
Type MIME <%= photo.image[:original].mime_type %>
Dimensions <%= "#photo.image[:original].widthx#photo.image[:original].height" %>

<% end %>

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

En outre, il y a un dimensions méthode disponible qui renvoie un tableau contenant la largeur et la hauteur (par exemple, [500, 750]).

Passer au nuage

Les développeurs choisissent souvent les services cloud pour héberger les fichiers téléchargés, et Shrine présente une telle possibilité. Dans cette section, je vais vous montrer comment télécharger des fichiers sur Amazon S3..

Dans un premier temps, incluez deux autres joyaux dans le Gemfile:

gem "aws-sdk", groupe "~> 2.1": le développement ne se termine pas avec "dotenv-rails"

aws-sdk doit fonctionner avec le SDK de S3, tandis que dotenv-rails sera utilisé pour gérer les variables d'environnement en développement..

installation groupée

Avant de continuer, vous devez vous procurer une paire de clés pour accéder à S3 via une API. Pour l'obtenir, connectez-vous (ou inscrivez-vous) à la console Amazon Web Services et accédez à Informations d'identification de sécurité> Utilisateurs. Créez un utilisateur autorisé à manipuler des fichiers sur S3. Voici la politique simple présentant un accès complet à S3:

"Version": "2016-11-14", "Instruction": ["Effet": "Autoriser", "Action": "s3: *", "Ressource": "*"]

Téléchargez la paire de clés de l'utilisateur créé. Alternativement, vous pouvez utiliser des clés d’accès root, mais je fortement décourager vous de le faire car il est très dangereux.

Créez ensuite un compartiment S3 pour héberger vos fichiers et ajoutez un fichier à la racine du projet pour héberger votre configuration:

.env

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

Ne jamais exposer ce fichier au public et assurez-vous de l'exclure de Git:

.gitignore

.env

Maintenant modifiez la configuration globale de Shrine et introduisez un nouveau stockage:

config / initializers / shrine.rb

require "sanctuaire" require "sanctuaire / stockage / s3" s3_options = clé_accès: ENV ['S3_KEY'], secret_access_key: ENV ['S3_SECRET'], région: ENV ['S3_REGION'], bucket: ENV ['S3_BUCKET'] , Shrine.storages = cache: Shrine :: Storage :: FileSystem.new ("public", préfixe: "uploads / cache"), store: Shrine :: Storage :: S3.new (préfixe: "store", ** s3_options),

C'est tout! Aucune modification ne doit être apportée aux autres parties de l'application et vous pouvez tester ce nouveau stockage immédiatement. Si vous recevez des erreurs de la part de S3 liées à des clés incorrectes, assurez-vous d’avoir copié avec précision la clé et le secret, sans espace ni fin et sans symboles spéciaux invisibles..

Conclusion

Nous sommes arrivés à la fin de cet article. Si tout va bien, vous êtes maintenant très confiant dans l’utilisation de Shrine et avez hâte de l’utiliser dans l’un de vos projets. Nous avons discuté de nombreuses fonctionnalités de ce joyau, mais il y en a bien plus, comme la possibilité de stocker un contexte supplémentaire avec des fichiers et le mécanisme de téléchargement direct.. 

Par conséquent, parcourez la documentation de Shrine et son site Web officiel, qui décrit en détail tous les plugins disponibles. Si vous avez d'autres questions à propos de ce joyau, n'hésitez pas à les poster. Je vous remercie de rester avec moi et à bientôt!