Cela faisait pas mal de temps que je voulais réussir à faire un petit outil (appelé très simplement Tar/Gzip Online Tools) pour compresser et archiver des fichiers en ligne. Le but était, au départ, de faire un outil permettant de compresser des fichiers, et un autre outil permettant de les décompresser. A la manière de Wobzip, par exemple. Bref, le problème, c’est que je suis et reste limité par le datastore, et par la plateforme AppEngine, qui empèche d’écrire quoi que ce soit. Loin de moi l’idée de dire du mal de cette contrainte : je souhaite juste attirer l’attention sur un aspect qui complique un tout petit peu la chose.
Très rapidement, j’ai donc fait un outil permettant d’archiver (rassembler des fichiers dans un même fichier, qui conserver l’arborescence de ceux-ci), grâce à la librairie tarfile de python. La difficulté a été pour le fait que tous les tutoriels donnés sur internet considéraient que l’on pouvait écrire sur le disque (alors que là, non). Il a donc fallu jouer avec la création de fichiers stream (StringIO.StringIO), ce qui m’a permit de découvrir l’existence de la librairie cStringIO, qui, selon le site de Python, a les même fonctionnalités que StringIO, sauf qu’elle est plus rapide. Notez que j’ai été débloqué dans mes recherches via ce thread, ainsi que par celui-là.
J’ai eu beaucoup de mal à faire fonctionner le zip (en utilisant ZipFile) et le gzip, dans la mesure où gzip n’est pas un archiveur, mais seulement un compresseur. C’est à dire qu’on ne peut normalement lui donner qu’un seul fichier, donc il va compresser le contenu. Zip, lui, fait archiveur aussi, mais je n’ai jamais réussi à le faire fonctionner. Si vous avez déjà réussi à faire fonctionner du python+zip de plusieurs fichiers (quand il y en a un, ça va, c’est quand il y en a plusieurs qu’il y a des problèmes), je suis preneur de votre code.
Décompression
Par contre, en ce qui concerne la décompression des archives, je ne pouvais pas gérer celà sans passer par le DataStore, ou bien il aurait fallu que je passe par une API stockant les fichiers à l’extérieur. En gros, soit je stocke les fichiers dans le DataStore, auquel cas c’est peu fantastique (et encore, dans l’idéal, je pourrais les stocker dans MemCached : c’est une piste à creuser ! Je m’y atèle dès ce soir.), car le DataStore est assez sensible en terme de quantité de données stockées, et de « taille de fichier » à mettre dedans (1 Mo maximum). C’est donc la raison pour laquelle j’ai abandonné l’idée dans un premier temps.
Edit : Depuis hier, quand j’ai écris ces lignes, il y a eu quelques améliorations :
D’abord, le programme dé-gzippe parfaitement (en fait, je lui ai donné des tar.gz et des fichiers normaux pour tester, ça fonctionne pas mal). Enfin je pense. J’observe cependant des différences entre le nombre d’octets pour le tar créé à partir du tar.gz et le tar que j’aurais créé moi-même (la quantité d’octet n’est pas la même). Je soupçonne des différences entre les librairies utilisées, qui ne seraient pas les même entre la librairie tarfile et celle qu’utilise Gnome/Ubuntu.
Ensuite, le programme reconnait également quand le fichier porte l’extension .tar, auquel cas il liste les fichiers contenus dans le tar. Je n’arrive pas à gérer parfaitement le cas où il y aurais une arborescence de fichiers, car il faudrait reproduire cette arborescence en ligne (et ça, c’est violent), mais je parviens quand même à afficher la liste des fichiers (il faut tester pour voir, ou regarder sur le screenshot ci-dessous, où l’archive contenait entre-autres un dossier « AnaliZ » avec des fichiers dedans). En cliquant sur chaque fichier, j’enclenche le téléchargement direct de celui-ci. Bref, ça marche (même si ces petites subtilités ternissent la marche correcte du script).
Fichiers de grande taille dans Memcached et/ou le DataStore
Enfin, et c’est là que c’est beau, pour chaque fichier affiché, on peut le télécharger. Notez que, comme je n’enclenche pas le téléchargement directement après l’upload, il faut que je passe par un stockage interne des fichiers. Hors, ceux-ci peuvent souvent être amenés à faire plus de 1 Mo, c’est bien normal (quand on sait qu’on atteint facilement des taux de compression de 70% avec des fichiers texte, ça ne m’étonne pas du tout qu’un fichier compressé de 500ko puisse faire, après décompression, une taille de 1,6 Mo, par exemple), donc je dois gérer le stockage de fichier d’une taille supérieure à 1 Mo. Mais AppEngine limite à 1 Mo la taille des données stockées par instance dans MemCached ou le DataStore. J’ai donc découpé la string d’octets pour que chaque partie fasse au maximum 1 Mo et rentre dans une instance de MemCached. Oui, j’ai utilisé MemCached parce que je ne veux pas charger mon DataStore pour rien (les données décompressées n’ont pas pour but de rester longtemps disponibles, donc elles n’ont rien à faire dans le DataStore, et je les mets dans Memcached pour une durée d’environ une heure, ce qui laisse amplement le temps de les télécharger !).
Bon, voilà, donc au final, ça fonctionne correctement pour les .tar et les .gz, ce pour une taille de fichier entrant limitée par HTML (c’est à dire 2 Mo pour les navigateurs qui ne sont pas en HTML5, et davantage pour les autres… personnellement, j’ai testé avec 8Mo sur mon serveur en local, et celui-ci n’a pas bronché).