Go to Hackademy website

Dragonfly et Fineuploader

Nicolas Zermati

Posté par Nicolas Zermati dans les catégories outils

Aujourd’hui, je voudrais partager une astuce pour faire fonctionner Dragonfly et Fineuploader ensemble.

Fineuploader permet de fournir une expérience d’envoi de fichier intuitive et visuelle pour l’utilisateur. Dragonfly permet, grâce à un middleware Rack, de rendre intuitive la manipulation des fichiers pour le developpeur.

Récemment, pour envoyer des fichiers de manière asynchrone, j’ai utilisé ces deux outils. Et, ô surprise, cela ne s’est pas passé comme prévu.

Dragonfly seul

Dragonfly s’utilise très bien sans fineuploader. Prenons le cas où nous avons un modèle simplifié de pièce jointe suivant : Attachment. Les colonnes attendues pour la table attachments sont : id, file_uid et file_name.

# app/models/attachment.rb
class Attachment < ActiveRecord::Base
  file_accessor :file
end

Voilà maintenant deux extraits du controlleur et de la vue associée à la création.

<%# app/views/attachments/new.html.erb %>
<% form_for @attachment, :html => {:multipart => true} do |f| %>
  <%= f.file_field :file %>
  <%= f.submit t('.submit') %>
<% end %>
# app/controllers/attachments_controller.rb
def new
  @attachment = Attachment.new
end

def create
  @attachment = Attachment.new(params[:attachment])
  @attachment.save ? redirect_to(action: index, notice: I18n.t('...')) : render(:new)
end

Comme on peut le voir, l’action de création est très simple. Dragonfly s’occupera de remplir les colonnes file_uid et file_name de la base de donnée ainsi que de stocker le fichier présent dans la requête.

Bien sûr, il faut configurer Dragonfly pour définir, entre autre, les répertoires de sauvegarde des fichiers ainsi que du cache.

Fineuploader seul

Fineuploader est un outil écrit en Javascript. Il n’a pas de dépendance vers d’autres bibliothèques telle que JQuery. L’outil est complet et offre un bon niveau de personnalisation en terme de style comme de comportement.

Me concernant, j’utilise plutôt FineUploaderBasic qui laisse le style ainsi que le comportement entièrement à la charge du programmeur.

Fineuploader est aussi cross-navigateur puisqu’il dispose de deux back-ends : un premier basé sur FormData et un second basé sur les iframes pour les navigateurs ne supportant pas le premier. Dans les deux cas (FormData ou iframe), Fineuploader va passer les paramètres de la requête HTTP dans l’URL et le fichier à envoyer en binaire dans le corps.

Coté serveur on se retrouve donc avec le nom du fichier envoyé dans paramètres dit GET : ?qqfile=mon-image.png et le contenu du fichier directement dans le corps du message.

Si l’on utilise Internet Explorer (ou tout autre navigateur ne supportant pas FormData), c’est encore différent puisqu’on a le nom du fichier dans la query string (toujours en GET) et le contenu du fichier dans le corps mais cette fois au format Multipart.

Dragonfly + Fineuploader

Pour adapter notre action de création à Fineuploader (utilisant FormData) on trouvera ce genre de code :

path = File.join(TEMPORARY_FILE_PATH, params[:qqfile])
file = File.new(path, 'wb')
file.write(request.body.read)
file.close

attachment = Attachment.new(file: file)

La pratique ci-dessus n’est pas à recommander. Dragonfly exige que ses paramètres soient passés dans le corps de la requête en respectant le format multipart. Mieux vaut utiliser l’option request.forceMultipart de Fineuploader qui permettra, même en utilisant FormData, d’encoder le corps de la requête au format multipart.

Même si les paramètres issus de la query string et du corps sont fusionnés dans la variable params dans Rails, j’ai constaté que le middleware de Dragonfly ne fonctionait que si tous ses paramètres se trouvaient dans le corps de la requête. Or, on a vu que Fineuploader séparait les paramètres entre la query string et le corps.

Patch

Le patch que je vous propose attaque Fineuploader plutôt que Dragonfly pour différentes raisons. Dans un premier temps, Fineuploader est beaucoup plus simple que Dragonfly (en termes d’architecture et de nombre de lignes). Ensuite, cela me semble plus cohérent de passer tous les paramètres en utilisant le même type de communication.

Je suis bien conscient que Fineuploader utilise sans doute ces mécanismes pour optimiser la taille de ses requêtes et que je vais à l’encontre de ce principe.

Voici donc le fameux patch (gist et raw) qui va grouper tous les paramètres de Fineuploader dans le corps d’une requête au format multipart (tant FormData qu’iframe) :

Pensez simplement à utiliser l’option request.forceMultipart et vous pourrez utiliser Dragonfly de la manière la plus transparente qui soit, quelque soit le navigateur.

Conclusion

J’espère que cette astuce vous aura été utile et vous aidera à mieux comprendre le fonctionnement de Fineuploader. N’hésitez pas à partager vos retours d’expériences sur l’envoi asynchrone de fichiers avec Rails : quels outils utilisez-vous, avez-vous rencontré des difficultés ?

L’équipe Synbioz.

Libres d’être ensemble.

Articles connexes

Un plugin Vim à la mimine

03/01/2019

Dans l’article précédent, intitulé une assez bonne intimité, je vous présentais GPG et le chiffrement de courriels. Nous avons alors remarqué que le contenu d’un courriel était encodé de sorte que le...

Une assez bonne intimité

20/12/2018

Si vous êtes utilisateur de MacOS, il y a de fortes chances que vous utilisiez Apple Mail pour échanger des courriels. Et comme vous êtes sensible à la confidentialité des informations que vous...

Tests end to end avec Jest et Puppeteer

05/07/2018

Dans cet article, on va partir sur des tests end to end. Pour planter le décor, un test end to end (e2e) est un test logiciel qui a pour but de valider que le système testé répond correctement à un...

Chasser les requêtes N+1 avec Bullet

05/04/2018

Aujourd’hui nous allons parler des requêtes N+1 dans une application Rails : vous savez ces lignes quasiment identiques, qui s’ajoutent de manière exponentielle aux logs, dès lors que l’on appelle...