Comment utiliser FilePond avec Active Storage de Rails
FilePond est une magnifique bibliothèque JavaScript pour gérer les téléchargements de fichiers écrite par Rik Schennink.
J’ai utilisé cette bibliothèque dans divers projets Ruby on Rails lorsque je voulais une expérience de téléchargement fluide associée à d’excellents visuels d’interface.
Dans cet article, je vais expliquer comment intégrer FilePond avec Rails, et je décrirai et expliquerai certains de mes choix techniques au fur et à mesure. Ce tutoriel suppose également une connaissance de base d’Active Storage. Si vous n’êtes pas familier avec cette partie de Rails, veuillez consulter le guide officiel Rails à https://guides.rubyonrails.org/active_storage_overview.html.
Le code de démonstration pour ce tutoriel est situé à https://github.com/Code-With-Rails/filepond-demo. Je vous recommande de cloner ce dépôt et d’exécuter la démo à partir de là.
Ce Que Nous Construisons
Pour intégrer FilePond avec notre application, il y a deux parties :
- Premièrement, nous devrons ajouter la bibliothèque JavaScript FilePond et l’activer sur notre balise HTML d’entrée de fichier.
- Deuxièmement, nous devrons intégrer FilePond avec notre application Rails. Spécifiquement, nous voulons qu’Active Storage gère les téléchargements de fichiers.
Pour accomplir la première partie, nous utiliserons du JavaScript vanilla. Pour la deuxième partie, nous tirerons parti de la bibliothèque JavaScript existante d’Active Storage et activerons les Téléchargements Directs. Pour accommoder certains endpoints de serveur spécifiques que FilePond requiert, nous créerons un contrôleur personnalisé.
Installation de FilePond
Pour ce tutoriel, nous commencerons avec une application Rails 7.0.x. Cela signifie que nous utiliserons importmap-rails pour ajouter nos dépendances JavaScript.
bin/bundle add importmap-rails
bin/rails importmap:install
Ensuite, nous devrons ajouter la dépendance pour FilePond avec la commande suivante :
bin/rails importmap pin filepond
Nous voulons précharger FilePond, donc dans le fichier config/importmap.rb, modifiez le fichier comme ceci :
# Avant
pin 'filepond', to: 'https://ga.jspm.io/npm:filepond@4.30.4/dist/filepond.js'
# Après
pin 'filepond', to: 'https://ga.jspm.io/npm:filepond@4.30.4/dist/filepond.js', preload: true
Les Bases de FilePond
Pour initialiser FilePond, nous devrons d’abord avoir un élément HTML d’entrée de fichier. Notre formulaire ressemblera à quelque chose comme ceci :
<%= form_with model: @user, url: update_avatar_path, method: :post do |f| %>
<%= f.label :avatar, 'Mettez à jour votre avatar' %>
<!-- Nous transformerons cette entrée en un pond -->
<%= f.file_field :avatar, class: 'filepond', direct_upload: true %>
<%= f.button 'Mettre à jour' %>
<% end %>
Le code JavaScript correspondant ressemblera à ceci :
// application.js
const input = document.querySelector('.filepond')
FilePond.create(input)
C’est le minimum de ce dont vous avez besoin pour que FilePond convertisse une simple balise d’entrée de fichier en widget.
Spécifiquement pour ce que nous construisons ici, nous implémenterons les éléments suivants :
- Configurer FilePond pour effectuer des téléchargements directs (en utilisant la bibliothèque JavaScript d’Active Storage) vers des fournisseurs cloud
- Configurer FilePond et notre application pour permettre le téléchargement avec une URL distante uniquement
- Configurer notre application pour purger les fichiers non attachés (blobs) lorsque les utilisateurs cliquent sur annuler (défaire) dans le widget FilePond
Application Rails Simple
Nous allons d’abord configurer notre modèle de données pour qu’Active Storage puisse associer les pièces jointes.
bin/rails g model User name:string
bin/rails db:migrate
Dans le fichier user.rb, mettons à jour le modèle comme ceci :
class User < ApplicationRecord
has_one_attached :avatar do |attachable|
attachable.variant :thumb, resize_to_limit: [100, 100]
end
end
Intégration de FilePond
Configurons FilePond pour utiliser la fonctionnalité de Téléchargement Direct d’Active Storage :
// app/javascript/application.js
FilePond.setOptions({
server: {
process: (fieldName, file, metadata, load, error, progress, abort, transfer, options) => {
const uploader = new DirectUpload(file, directUploadUrl, {
directUploadWillStoreFileWithXHR: (request) => {
request.upload.addEventListener(
'progress',
event => progress(event.lengthComputable, event.loaded, event.total)
)
}
})
uploader.create((errorResponse, blob) => {
if (errorResponse) {
error(`Quelque chose s'est mal passé : ${errorResponse}`)
} else {
const hiddenField = document.createElement('input')
hiddenField.setAttribute('type', 'hidden')
hiddenField.setAttribute('value', blob.signed_id)
hiddenField.name = input.name
document.querySelector('form').appendChild(hiddenField)
load(blob.signed_id)
}
})
return { abort: () => abort() }
},
fetch: {
url: './filepond/fetch',
method: 'POST'
},
revert: {
url: './filepond/remove'
},
headers: {
'X-CSRF-Token': document.head.querySelector("[name='csrf-token']").content
}
}
})
Pour les endpoints fetch et revert, nous avons besoin d’un contrôleur personnalisé :
# app/controllers/filepond_controller.rb
require 'open-uri'
class FilepondController < ApplicationController
def fetch
uri = URI.parse(raw_post)
url = uri.to_s
blob = ActiveStorage::Blob.create_and_upload!(
io: URI.open(uri),
filename: URI.parse(url).path.parameterize
)
if blob.persisted?
redirect_to rails_service_blob_path(blob.signed_id, blob.filename)
else
head :unprocessable_entity
end
end
def remove
signed_id = raw_post
blob = ActiveStorage::Blob.find_signed(signed_id)
if blob
blob.purge
head :ok
else
head :not_found
end
end
private
def raw_post
request.raw_post
end
end
Et les routes :
# config/routes.rb
Rails.application.routes.draw do
post 'filepond/fetch', to: 'filepond#fetch'
delete 'filepond/remove', to: 'filepond#remove'
end
Conclusion
FilePond offre une excellente interface utilisateur pour fournir un retour d’information aux téléchargements des utilisateurs. L’intégrer avec Active Storage de Rails n’est pas difficile, mais nécessite un peu de personnalisation.
Ce tutoriel présente une façon d’intégrer FilePond et applique autant de Rails vanilla que possible. Dans la prochaine partie, j’utiliserai les méthodes d’implémentation ci-dessus et transformerai tout cela en une gem que nous pourrons réutiliser sans avoir à réimplémenter cela à chaque fois.