Gérer les dossiers de stockages de données

Première publication : 2008-04-29
Tags : railsruby

Voici une petite note à mon attention, directement inspirée de l’article de Jamis Buck et du code source de attachement_fu (un superbe plugin Rails pour l’upload de fichiers joints).

Dans une application qui stocke des fichiers uploadés par les utilisateurs, je souhaite que les fichiers soient organisés proprement et que chaque compte ait son propre dossier de premier niveau au lieu de tout mélanger + ou - en vrac.

Le principe de attachement_fu est celui-ci, avec par exemple un fichier ayant l’ID 12345 dans la base de données : /0001/2345/mon_fichier.jpg

NB : tout est relatif à RAILS_ROOT/public/fichiers_joints

Comme chaque fichier est lié à un enregistrement dans la base de données, il n’y a pas de risque d’écrasement, mais si je dois sortir rapidement tous les fichiers d’un compte par FTP, je suis très embêté.

J’aimerais faire ceci : /compte_482/0001/2345/mon_fichier.jpg

Mais comme les fichiers sont eux-mêmes classés par “groupe” au sein d’un compte et que le nombre de fichiers ne devrait pas excéder quelques petits milliers par groupe (le plus souvent quelques centaines), je peux simplifier : /compte_482/groupe_047/12345/mon_fichier.jpg

La limite est donc à ~100 000 fichiers par groupe, ~1 000 groupes par compte et ~1 000 comptes.
Si ma petite appli atteint 5% de ces volumes je serais déjà assez riche.

Et puis après tout, si se limiter à 100 000 fichiers par groupe est trop réducteur, reprenons le découpage en sous-dossiers selon l’ID sur X chiffres.
Et si le nombre de groupes par client ou le nombre de client devait exploser, on y ferait de même.

Bon, passons au choses sérieuses ; coder la méthode qui va surclasser celle de attachement_fu (exemple avec des découpes à 6 chiffres).

# La méthode doit renvoyer un tableau où chaque élément
# est un segment du path à utiliser, à partir du dossier
# principal de stockage "RAILS_ROOT/public/fichiers_joints/".
# "attachment_path_id" est une méthode interne du plugin
def partitioned_path(*args)
  a = []
  a << ("%06d" % @client_id).scan(/.../)
  a << ("%06d" % @group_id).scan(/.../)
  a << ("%06d" % attachment_path_id).scan(/.../)
  a << args
end