Il y a quelque temps, j'ai écrit un article sur le téléchargement de fichiers avec Rails et Shrine expliquant comment introduire une fonctionnalité de téléchargement de fichier dans votre application Rails à l'aide de la gemme Shrine. Il existe cependant un ensemble de solutions similaires, et l'un de mes préférés est Dragonfly, une solution de téléchargement facile à utiliser pour Rails and Rack créée par Mark Evans..
Nous avons couvert cette bibliothèque au début de l’année dernière mais, comme pour la plupart des logiciels, il est utile de consulter de temps en temps les bibliothèques pour voir ce qui a changé et comment nous pouvons l’utiliser dans notre application.
Dans cet article, je vais vous guider dans la configuration de Dragonfly et vous expliquer comment utiliser ses principales fonctionnalités. Vous allez apprendre à:
Pour rendre les choses plus intéressantes, nous allons créer une petite application musicale. Il présentera des albums et des chansons associées pouvant être gérés et lus sur le site Web..
Le code source de cet article est disponible sur GitHub. Vous pouvez également consulter la démo de l'application.
Pour commencer, créez une nouvelle application Rails sans la suite de tests par défaut:
rails nouveau UploadingWithDragonfly -T
Pour cet article, j'utiliserai Rails 5, mais la plupart des concepts décrits s'appliquent également aux versions antérieures..
Notre petit site musical va contenir deux modèles: Album
et Chanson
. Pour l'instant, créons le premier avec les champs suivants:
Titre
(chaîne
) - contient le titre de l'albumchanteur
(chaîne
) l'interprète de l'albumimage_uid
(chaîne
) -un champ spécial pour stocker l’image d’aperçu de l’album. Ce champ peut être nommé comme vous voulez, mais il doit contenir le _uid
suffixe comme indiqué par la documentation Dragonfly.Créez et appliquez la migration correspondante:
rails g model Titre de l'album: string singer: string image_uid: string rails db: migrate
Créons maintenant un contrôleur très générique pour gérer les albums avec toutes les actions par défaut:
classe AlbumsController < ApplicationController def index @albums = Album.all end def show @album = Album.find(params[:id]) end def new @album = Album.new end def create @album = Album.new(album_params) if @album.save flash[:success] = 'Album added!' redirect_to albums_path else render :new end end def edit @album = Album.find(params[:id]) end def update @album = Album.find(params[:id]) if @album.update_attributes(album_params) flash[:success] = 'Album updated!' redirect_to albums_path else render :edit end end def destroy @album = Album.find(params[:id]) @album.destroy flash[:success] = 'Album removed!' redirect_to albums_path end private def album_params params.require(:album).permit(:title, :singer) end end
Enfin, ajoutez les routes:
ressources: albums
Il est temps que Dragonfly se mette à l'honneur. Tout d'abord, ajoutez la gemme dans le Gemfile:
bijou 'libellule'
Courir:
bundle install rails produisent une libellule
Cette dernière commande créera un initialiseur nommé libellule.rb avec la configuration par défaut. Nous allons le mettre de côté pour l'instant, mais vous pouvez lire diverses options sur le site officiel de Dragonfly..
La prochaine chose importante à faire est d’équiper notre modèle des méthodes de Dragonfly. Ceci est fait en utilisant le libellule_accesseur
:
libellule_accesseur: image
Notez que je dis ici :image
-il est directement lié à la image_uid
colonne que nous avons créée dans la section précédente. Si, par exemple, vous avez nommé votre colonne photo_uid
, puis le libellule_accesseur
méthode aurait besoin de recevoir :photo
comme argument.
Si vous utilisez Rails 4 ou 5, une autre étape importante consiste à marquer le :image
domaine (pas : image_uid
!) comme autorisé dans le contrôleur:
params.require (: album) .permit (: titre,: chanteur,: image)
C’est à peu près cela: nous sommes prêts à créer des vues et à commencer à télécharger nos fichiers.!
Commencez avec la vue index:
Albums
<%= link_to 'Add', new_album_path %>
Maintenant le partiel:
Il y a deux méthodes de libellule à noter ici:
album.image.url
renvoie le chemin vers l'image.album.image_stored?
dit si l'enregistrement a un fichier téléchargé en place.Ajoutez maintenant les nouvelles pages et les pages d'édition:
Ajouter un album
<%= render 'form' %>
modifier <%= @album.title %>
<%= render 'form' %>
<%= form_for @album do |f| %><%= f.label :title %> <%= f.text_field :title %><%= f.label :singer %> <%= f.text_field :singer %><%= f.label :image %> <%= f.file_field :image %><%= f.submit %> <% end %>
La forme n'a rien d'extraordinaire, mais une fois encore, notez que nous disons :image
, ne pas : image_uid
, lors du rendu de l'entrée du fichier.
Maintenant, vous pouvez démarrer le serveur et tester la fonctionnalité de téléchargement!
Ainsi, les utilisateurs peuvent créer et éditer des albums, mais il y a un problème: ils n'ont aucun moyen de supprimer une image, seulement de la remplacer par une autre. Heureusement, il est très facile de résoudre ce problème en introduisant une case à cocher "Supprimer l'image":
<% if @album.image_thumb_stored? %> <%= image_tag(@album.image.url, alt: @album.title) %> <%= f.label :remove_image %> <%= f.check_box :remove_image %> <% end %>
Si l’album est associé à une image, elle est affichée et une case à cocher est affichée. Si cette case est cochée, l'image sera supprimée. Notez que si votre champ est nommé photo_uid
, alors la méthode correspondante pour supprimer l'attachement sera Retirer photo
. Simple, n'est ce pas?
La seule autre chose à faire est de permettre au remove_image
Attribuer dans votre contrôleur:
params.require (: album) .permit (: titre,: chanteur,: image,: remove_image)
À ce stade, tout fonctionne bien, mais nous ne vérifions pas du tout la saisie de l'utilisateur, ce qui n'est pas particulièrement intéressant. Par conséquent, ajoutons des validations pour le modèle Album:
valide: titre, présence: vrai valide: chanteur, présence: vrai valide: image, présence: vrai propriété_valide: largeur, de: image, dans: (0… 900)
validates_property
est la méthode Dragonfly qui peut vérifier divers aspects de votre pièce jointe: vous pouvez valider l’extension, le type MIME, la taille, etc. d’un fichier..
Créons maintenant un partiel générique pour restituer les erreurs trouvées:
<% if object.errors.any? %><% end %>Les erreurs suivantes ont été trouvées:
<% object.errors.full_messages.each do |msg| %>
- <%= msg %>
<% end %>
Employer cette partielle dans le formulaire:
<%= form_for @album do |f| %> <%= render 'shared/errors', object: @album %> <%#… %> <% end %>
Style les champs avec des erreurs un peu pour les décrire visuellement:
.field_with_errors display: inline; étiquette couleur: rouge; input background-color: lightpink;
Une fois les validations introduites, nous nous heurtons à un autre problème (scénario assez typique, non?): Si l’utilisateur a commis des erreurs en remplissant le formulaire, il devra à nouveau choisir le fichier après avoir cliqué sur le bouton Soumettre bouton.
Dragonfly peut également vous aider à résoudre ce problème en utilisant un retenu_ *
champ caché:
<%= f.hidden_field :retained_image %>
N'oubliez pas de permettre également ce champ:
params.require (: album) .permit (: titre,: chanteur,: image,: remove_image,: held_image)
Maintenant, l'image sera persistée entre les demandes! Le seul petit problème, cependant, est que l'entrée de téléchargement de fichier affichera toujours le message "choisir un fichier", mais cela peut être corrigé avec un style et un tiret de JavaScript..
Les images téléchargées par nos utilisateurs peuvent avoir des dimensions très différentes, ce qui peut (et aura probablement) un impact négatif sur la conception du site. Vous voudrez probablement réduire les images à des dimensions fixes, ce qui est bien sûr possible en utilisant le largeur
et la taille
modes. Ce n’est cependant pas une approche optimale: le navigateur devra tout de même télécharger les images en taille réelle puis les réduire..
Une autre option (qui est généralement bien meilleure) consiste à générer des vignettes d’image avec certaines dimensions prédéfinies sur le serveur. C'est très simple à réaliser avec Dragonfly:
250x250
est, bien sûr, les dimensions, alors que #
est la géométrie qui signifie "redimensionner et rogner si nécessaire pour maintenir les proportions avec la gravité au centre". Vous pouvez trouver des informations sur d'autres géométries sur le site de Dragonfly.
le pouce
Cette méthode est optimisée par ImageMagick, une excellente solution pour créer et manipuler des images. Par conséquent, afin de voir la démo de travail localement, vous devez installer ImageMagick (toutes les principales plates-formes sont prises en charge)..
La prise en charge d'ImageMagick est activée par défaut dans l'initialiseur de Dragonfly:
plugin: imagemagick
Des vignettes sont maintenant générées, mais elles ne sont stockées nulle part. Cela signifie que chaque fois qu'un utilisateur visite la page d'albums, les vignettes seront régénérées. Il existe deux manières de résoudre ce problème: en les générant après l'enregistrement de l'enregistrement ou en effectuant une génération à la volée..
La première option consiste à introduire une nouvelle colonne pour stocker la vignette et à peaufiner la libellule_accesseur
méthode. Créez et appliquez une nouvelle migration:
rails g migration add_image_thumb_uid_to_albums image_thumb_uid: chaîne rails db: migrate
Maintenant modifiez le modèle:
dragonfly_accessor: image do copy_to (: image_thumb) | a | a.thumb ('250x250 #') end libellule_accesseur: image_thumb
Notez que maintenant le premier appel à libellule_accesseur
envoie un bloc qui génère réellement la vignette pour nous et le copie dans le image_thumb
. Maintenant, utilisez simplement le image_thumb
méthode dans vos vues:
<%= image_tag(album.image_thumb.url, alt: album.title) if album.image_thumb_stored? %>
Cette solution est la plus simple, mais elle n’est pas recommandée par la documentation officielle et, pire encore, au moment de la rédaction du présent document, elle ne fonctionnait pas avec la retenu_ *
des champs.
Par conséquent, laissez-moi vous montrer une autre option: générer des vignettes à la volée. Cela implique de créer un nouveau modèle et de peaufiner le fichier de configuration de Dragonfly. Tout d'abord, le modèle:
rails g model Thumb uid: chaîne job: chaîne rake db: migrate
le les pouces
table hébergera vos vignettes, mais elles seront générées à la demande. Pour ce faire, nous devons redéfinir la url
méthode à l'intérieur de l'initialiseur Dragonfly:
Dragonfly.app.configure do define_url do | application, travail, opte | thumb = Thumb.find_by_job (job.signature) si thumb app.datastore.url_for (thumb.uid,: scheme => 'https') else app.server.url_for (job) end end before_serve do | job, env | uid = job.store Thumb.create! (: uid => uid,: job => job.signature) end #… end
Ajoutez maintenant un nouvel album et visitez la page racine. La première fois que vous le faites, le résultat suivant sera imprimé dans les journaux:
DRAGONFLY: commande shell: "convert" "chemin_proche / public / système / libellule / développement / 2017/02/08 / 3z5p5nvbmx_Folder.jpg" "-resize" "250x250 ^^" "-gravité" "Centre" "-" " 250x250 + 0 + 0 "" + repage "" some_path / 20170208-1692-1xrqzc9.jpg "
Cela signifie effectivement que la vignette est générée pour nous par ImageMagick. Si vous rechargez la page, cependant, cette ligne n'apparaîtra plus, ce qui signifie que la vignette a été mise en cache! Vous pouvez lire un peu plus sur cette fonctionnalité sur le site de Dragonfly.
Vous pouvez pratiquement manipuler vos images après leur téléchargement. Cela peut être fait à l'intérieur du after_assign
rappeler. Convertissons par exemple toutes nos images au format JPEG avec une qualité de 90%:
dragonfly_accessor: image do after_assign | a | a.encode! ('jpg', '-quality 90') end
Vous pouvez effectuer de nombreuses autres actions: faire pivoter et rogner les images, encoder avec un format différent, écrire du texte dessus, les mélanger avec d'autres images (par exemple, pour placer un filigrane), etc. Pour voir d'autres exemples, reportez-vous à la section ImageMagick sur le site Web de Dragonfly.
Bien sûr, la partie principale de notre site musical est constituée de chansons, ajoutons-les maintenant. Chaque chanson a un titre et un fichier musical, et elle appartient à un album:
rails g model Album de morceaux: appartient à titre: chaine track_uid: string rails db: migrate
Connectez les méthodes de libellule, comme nous l'avons fait pour le Album
modèle:
libellule_accesseur: piste
N'oubliez pas d'établir un a beaucoup
relation:
has_many: chansons, dépendant:: détruire
Ajouter de nouveaux itinéraires. Une chanson existe toujours dans le cadre d'un album, je vais donc imbriquer ces routes:
ressources: les albums font des ressources: chansons, seulement: [: nouveau,: créer] fin
Créez un contrôleur très simple (encore une fois, n'oubliez pas d'autoriser le Piste
champ):
classe SongsController < ApplicationController def new @album = Album.find(params[:album_id]) @song = @album.songs.build end def create @album = Album.find(params[:album_id]) @song = @album.songs.build(song_params) if @song.save flash[:success] = "Song added!" redirect_to album_path(@album) else render :new end end private def song_params params.require(:song).permit(:title, :track) end end
Affichez les chansons et un lien pour en ajouter une nouvelle:
<%= @album.title %>
par <%= @album.singer %>
<%= link_to 'Add song', new_album_song_path(@album) %><%= render @album.songs %>
Codez le formulaire:
Ajouter une chanson à <%= @album.title %>
<%= form_for [@album, @song] do |f| %><%= f.label :title %> <%= f.text_field :title %><%= f.label :track %> <%= f.file_field :track %><%= f.submit %> <% end %>
Enfin, ajoutez le _chanson partiel:
Ici j'utilise le HTML5 l'audio
balise, qui ne fonctionnera pas pour les anciens navigateurs. Donc, si vous souhaitez supporter de tels navigateurs, utilisez un polyfill.
Comme vous le voyez, l'ensemble du processus est très simple. Dragonfly ne se soucie pas vraiment du type de fichier que vous souhaitez télécharger; tout ce que vous devez faire est de fournir un libellule_accesseur
méthode, ajouter un champ approprié, l'autoriser et restituer une balise d'entrée de fichier.
Lorsque j'ouvre une liste de lecture, je m'attends à voir des informations supplémentaires sur chaque chanson, telles que sa durée ou son débit. Bien sûr, par défaut, ces informations ne sont stockées nulle part, mais nous pouvons y remédier assez facilement. Dragonfly nous permet de fournir des données supplémentaires sur chaque fichier téléchargé et de les récupérer plus tard en utilisant le méta
méthode.
Les choses sont toutefois un peu plus complexes lorsque nous travaillons avec de l'audio ou de la vidéo, car pour extraire leurs métadonnées, une gem streamio-ffmpeg spéciale est nécessaire. Ce bijou, à son tour, repose sur FFmpeg, vous devez donc l'installer sur votre PC..
Ajouter streamio-ffmpeg
dans le Gemfile:
gem 'streamio-ffmpeg'
Installez-le:
installation groupée
Maintenant nous pouvons employer le même after_assign
rappel déjà vu dans les sections précédentes:
dragonfly_accessor: track do after_assign do | a | chanson = FFMPEG :: Movie.new (a.path) mm, ss = chanson.duration.divmod (60) .map | n | n.to_i.to_s.rjust (2, '0') a.meta ['duration'] = "# mm: # ss" a.meta ['bitrate'] = song.bitrate? song.bitrate / 1000: 0 end end
Notez que je me sers ici chemin
méthode, pas url
, car à ce stade, nous travaillons avec un fichier temporaire. Ensuite, nous extrayons simplement la durée de la chanson (conversion en minutes et secondes avec des zéros non significatifs) et son débit (conversion en kilo-octets par seconde).
Enfin, affichez les métadonnées dans la vue:
Si vous vérifiez le contenu sur le public / système / libellule dossier (l’emplacement par défaut pour héberger les téléchargements), vous remarquerez quelques .yml fichiers-ils stockent toutes les méta-informations au format YAML.
Le dernier sujet que nous aborderons aujourd'hui concerne la préparation de votre application avant son déploiement sur la plate-forme cloud Heroku. Le problème principal est que Heroku ne vous autorise pas à stocker des fichiers personnalisés (tels que les téléchargements). Nous devons donc nous appuyer sur un service de stockage en nuage comme Amazon S3. Heureusement, Dragonfly peut être intégré facilement.
Tout ce que vous avez à faire est d’enregistrer un nouveau compte sur AWS (si vous ne l’avez pas déjà), de créer un utilisateur autorisé à accéder aux compartiments S3 et d’écrire la paire de clés de l’utilisateur dans un emplacement sûr. Vous pouvez utiliser une paire de clés racine, mais c’est vraiment non recommandé. Enfin, créez un compartiment S3.
Pour revenir à notre application Rails, déposez un nouveau joyau:
groupe: production do gem 'libellule-s3_data_store' fin
Installez-le:
installation groupée
Puis ajustez la configuration de Dragonfly pour utiliser S3 dans un environnement de production:
si Rails.env.production? magasin de données: s3, bucket_name: ENV ['S3_BUCKET'], clé_accès_id: ENV ['S3_KEY'], secret_access_key: ENV ['S3_SECRET'], région: ENV ['S3_REGION'], url_scheme: 'https', autre date, chemin_racine: Rails.root.join ('public / system / libellule', Rails.env), racine du serveur: Rails.root.join ('public') end
Fournir ENV
variables sur Heroku, utilisez cette commande:
heroku config: add SOME_KEY = SOME_VALUE
Si vous souhaitez tester l'intégration avec S3 localement, vous pouvez utiliser une gemme telle que dotenv-rails pour gérer les variables d'environnement. Rappelez-vous cependant que votre paire de clés AWS ne doit pas être exposé publiquement!
L’absence de FFmpeg est un autre petit problème que j’ai rencontré lors de mon déploiement à Heroku. Le fait est que lors de la création d’une nouvelle application Heroku, elle dispose d’un ensemble de services couramment utilisés (par exemple, ImageMagick est disponible par défaut). D'autres services peuvent être installés sous forme d'additifs Heroku ou sous la forme de packs de construction. Pour ajouter un buildpack FFmpeg, exécutez la commande suivante:
les buildpacks heroku: ajoutez https://github.com/HYPERHYPER/heroku-buildpack-ffmpeg.git
Maintenant tout est prêt et vous pouvez partager votre application musicale avec le monde entier.!
Ce fut un long voyage, n'est-ce pas? Aujourd'hui, nous avons discuté de Dragonfly, une solution de téléchargement de fichiers dans Rails. Nous avons vu sa configuration de base, certaines options de configuration, la génération, le traitement et l’enregistrement de métadonnées. De plus, nous avons intégré Dragonfly au service Amazon S3 et préparé notre application en vue de son déploiement en production..
Bien sûr, nous n’avons pas abordé tous les aspects de Dragonfly dans cet article, assurez-vous donc de parcourir son site officiel pour trouver une documentation complète et des exemples utiles. Si vous avez d'autres questions ou si vous avez des exemples de code, n'hésitez pas à me contacter..
Merci de rester avec moi et à bientôt!